Skip to content

Commit

Permalink
implement allowzero pointer attribute
Browse files Browse the repository at this point in the history
closes #1953

only needed for freestanding targets.

also adds safety for `@intToPtr` when the address is zero.
  • Loading branch information
andrewrk committed Mar 25, 2019
1 parent 3306e43 commit 5eaead6
Show file tree
Hide file tree
Showing 18 changed files with 225 additions and 78 deletions.
1 change: 1 addition & 0 deletions doc/docgen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
std.zig.Token.Id.Keyword_use,
std.zig.Token.Id.Keyword_var,
std.zig.Token.Id.Keyword_volatile,
std.zig.Token.Id.Keyword_allowzero,
std.zig.Token.Id.Keyword_while,
=> {
try out.write("<span class=\"tok-kw\">");
Expand Down
44 changes: 36 additions & 8 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -1721,7 +1721,7 @@ test "comptime @intToPtr" {
}
}
{#code_end#}
{#see_also|Optional Pointers|@intToPtr|@ptrToInt#}
{#see_also|Optional Pointers|@intToPtr|@ptrToInt|C Pointers|Pointers to Zero Bit Types#}
{#header_open|volatile#}
<p>Loads and stores are assumed to not have side effects. If a given load or store
should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}.
Expand Down Expand Up @@ -1850,7 +1850,25 @@ fn foo(bytes: []u8) u32 {
}
{#code_end#}
{#header_close#}
{#see_also|C Pointers|Pointers to Zero Bit Types#}

{#header_open|allowzero#}
<p>
This pointer attribute allows a pointer to have address zero. This is only ever needed on the
freestanding OS target, where the address zero is mappable. In this code example, if the pointer
did not have the {#syntax#}allowzero{#endsyntax#} attribute, this would be a
{#link|Pointer Cast Invalid Null#} panic:
</p>
{#code_begin|test|allowzero#}
const std = @import("std");
const assert = std.debug.assert;

test "allowzero" {
var zero: usize = 0;
var ptr = @intToPtr(*allowzero i32, zero);
assert(@ptrToInt(ptr) == 0);
}
{#code_end#}
{#header_close#}
{#header_close#}

{#header_open|Slices#}
Expand Down Expand Up @@ -6635,9 +6653,13 @@ fn add(a: i32, b: i32) i32 { return a + b; }
{#header_close#}

{#header_open|@intToPtr#}
<pre>{#syntax#}@intToPtr(comptime DestType: type, int: usize) DestType{#endsyntax#}</pre>
<pre>{#syntax#}@intToPtr(comptime DestType: type, address: usize) DestType{#endsyntax#}</pre>
<p>
Converts an integer to a {#link|pointer|Pointers#}. To convert the other way, use {#link|@ptrToInt#}.
</p>
<p>
Converts an integer to a pointer. To convert the other way, use {#link|@ptrToInt#}.
If the destination pointer type does not allow address zero and {#syntax#}address{#endsyntax#}
is zero, this invokes safety-checked {#link|Undefined Behavior#}.
</p>
{#header_close#}

Expand Down Expand Up @@ -8128,6 +8150,11 @@ fn bar(f: *Foo) void {
{#header_close#}

{#header_open|Pointer Cast Invalid Null#}
<p>
This happens when casting a pointer with the address 0 to a pointer which may not have the address 0.
For example, {#link|C Pointers#}, {#link|Optional Pointers#}, and {#link|allowzero#} pointers
allow address zero, but normal {#link|Pointers#} do not.
</p>
<p>At compile-time:</p>
{#code_begin|test_err|null pointer casted to type#}
comptime {
Expand Down Expand Up @@ -8988,7 +9015,7 @@ Available libcs:
<ul>
<li>Linux x86_64</li>
<li>Windows x86_64</li>
<li>MacOS x86_64</li>
<li>macOS x86_64</li>
</ul>
{#header_close#}
{#header_open|Style Guide#}
Expand Down Expand Up @@ -9412,8 +9439,8 @@ PrefixOp
PrefixTypeOp
&lt;- QUESTIONMARK
/ KEYWORD_promise MINUSRARROW
/ ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
/ PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
/ ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
/ PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*

SuffixOp
&lt;- LBRACKET Expr (DOT2 Expr?)? RBRACKET
Expand Down Expand Up @@ -9564,6 +9591,7 @@ TILDE &lt;- '~' skip

end_of_word &lt;- ![a-zA-Z0-9_] skip
KEYWORD_align &lt;- 'align' end_of_word
KEYWORD_allowzero &lt;- 'allowzero' end_of_word
KEYWORD_and &lt;- 'and' end_of_word
KEYWORD_anyerror &lt;- 'anyerror' end_of_word
KEYWORD_asm &lt;- 'asm' end_of_word
Expand Down Expand Up @@ -9614,7 +9642,7 @@ KEYWORD_var &lt;- 'var' end_of_word
KEYWORD_volatile &lt;- 'volatile' end_of_word
KEYWORD_while &lt;- 'while' end_of_word

keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm
keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_anyerror / KEYWORD_asm
/ KEYWORD_async / KEYWORD_await / KEYWORD_break / KEYWORD_cancel
/ KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue
/ KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer
Expand Down
4 changes: 2 additions & 2 deletions src/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2668,7 +2668,7 @@ struct IrInstructionPtrType {
PtrLen ptr_len;
bool is_const;
bool is_volatile;
bool allow_zero;
bool is_allow_zero;
};

struct IrInstructionPromiseType {
Expand All @@ -2684,7 +2684,7 @@ struct IrInstructionSliceType {
IrInstruction *child_type;
bool is_const;
bool is_volatile;
bool allow_zero;
bool is_allow_zero;
};

struct IrInstructionGlobalAsm {
Expand Down
24 changes: 11 additions & 13 deletions src/analyze.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,9 @@ static const char *ptr_len_to_star_str(PtrLen ptr_len) {

ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
uint32_t bit_offset_in_host, uint32_t host_int_bytes)
uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero)
{
// TODO when implementing https://github.com/ziglang/zig/issues/1953
// move this to a parameter
bool allow_zero = (ptr_len == PtrLenC);
assert(ptr_len != PtrLenC || allow_zero);
assert(!type_is_invalid(child_type));
assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);

Expand Down Expand Up @@ -505,7 +503,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
bit_offset_in_host != 0 || allow_zero)
{
ZigType *peer_type = get_pointer_to_type_extra(g, child_type, false, false,
PtrLenSingle, 0, 0, host_int_bytes);
PtrLenSingle, 0, 0, host_int_bytes, false);
entry->type_ref = peer_type->type_ref;
entry->di_type = peer_type->di_type;
} else {
Expand Down Expand Up @@ -548,7 +546,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
}

ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) {
return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0);
return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false);
}

ZigType *get_promise_frame_type(CodeGen *g, ZigType *return_type) {
Expand Down Expand Up @@ -857,7 +855,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero)
{
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
PtrLenUnknown, 0, 0, 0);
PtrLenUnknown, 0, 0, 0, false);
ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);

slice_type_common_init(g, ptr_type, entry);
Expand All @@ -881,10 +879,10 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
{
ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
PtrLenUnknown, 0, 0, 0);
PtrLenUnknown, 0, 0, 0, false);
ZigType *bland_child_slice = get_slice_type(g, bland_child_ptr_type);
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false,
PtrLenUnknown, 0, 0, 0);
PtrLenUnknown, 0, 0, 0, false);
ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);

entry->type_ref = peer_slice_type->type_ref;
Expand Down Expand Up @@ -1419,7 +1417,7 @@ static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_

static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, 0, 0, 0);
PtrLenUnknown, 0, 0, 0, false);
ZigType *str_type = get_slice_type(g, ptr_type);
ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr);
if (type_is_invalid(result_val->type))
Expand Down Expand Up @@ -5336,7 +5334,7 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
const_val->special = ConstValSpecialStatic;
// TODO make this `[*]null u8` instead of `[*]u8`
const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, 0, 0, 0);
PtrLenUnknown, 0, 0, 0, false);
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
const_val->data.x_ptr.data.base_array.array_val = array_val;
const_val->data.x_ptr.data.base_array.elem_index = 0;
Expand Down Expand Up @@ -5481,7 +5479,7 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr
assert(array_val->type->id == ZigTypeIdArray);

ZigType *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type,
is_const, false, PtrLenUnknown, 0, 0, 0);
is_const, false, PtrLenUnknown, 0, 0, 0, false);

const_val->special = ConstValSpecialStatic;
const_val->type = get_slice_type(g, ptr_type);
Expand All @@ -5506,7 +5504,7 @@ void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue

const_val->special = ConstValSpecialStatic;
const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false,
ptr_len, 0, 0, 0);
ptr_len, 0, 0, 0, false);
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
const_val->data.x_ptr.data.base_array.array_val = array_val;
const_val->data.x_ptr.data.base_array.elem_index = elem_index;
Expand Down
4 changes: 3 additions & 1 deletion src/analyze.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ void emit_error_notes_for_ref_stack(CodeGen *g, ErrorMsg *msg);
ZigType *new_type_table_entry(ZigTypeId id);
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
bool is_volatile, PtrLen ptr_len,
uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
bool allow_zero);
uint64_t type_size(CodeGen *g, ZigType *type_entry);
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
Expand Down
31 changes: 22 additions & 9 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,7 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {
}

ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
ZigType *str_type = get_slice_type(g, u8_ptr_type);
return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0));
}
Expand Down Expand Up @@ -1515,7 +1515,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {


ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
ZigType *str_type = get_slice_type(g, u8_ptr_type);
LLVMValueRef global_slice_fields[] = {
full_buf_ptr,
Expand Down Expand Up @@ -3103,6 +3103,18 @@ static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executa
static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutable *executable, IrInstructionIntToPtr *instruction) {
ZigType *wanted_type = instruction->base.value.type;
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
if (!ptr_allows_addr_zero(wanted_type) && ir_want_runtime_safety(g, &instruction->base)) {
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(target_val));
LLVMValueRef is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, target_val, zero, "");
LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntBad");
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntOk");
LLVMBuildCondBr(g->builder, is_zero_bit, bad_block, ok_block);

LLVMPositionBuilderAtEnd(g->builder, bad_block);
gen_safety_crash(g, PanicMsgIdPtrCastNull);

LLVMPositionBuilderAtEnd(g->builder, ok_block);
}
return LLVMBuildIntToPtr(g->builder, target_val, wanted_type->type_ref, "");
}

Expand Down Expand Up @@ -3270,7 +3282,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,

if (have_init_expr) {
ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->var_type, false, false,
PtrLenSingle, var->align_bytes, 0, 0);
PtrLenSingle, var->align_bytes, 0, 0, false);
LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value);
gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val);
} else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) {
Expand Down Expand Up @@ -4160,7 +4172,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
return enum_type->data.enumeration.name_function;

ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
ZigType *u8_slice_type = get_slice_type(g, u8_ptr_type);
ZigType *tag_int_type = enum_type->data.enumeration.tag_int_type;

Expand Down Expand Up @@ -4953,7 +4965,7 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable,

ZigType *ptr_type = get_pointer_to_type_extra(g, type_struct_field->type_entry,
false, false, PtrLenSingle, field_align_bytes,
(uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes);
(uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes, false);

gen_assign_raw(g, field_ptr, ptr_type, value);
}
Expand All @@ -4969,7 +4981,7 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I
uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry);
ZigType *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry,
false, false, PtrLenSingle, field_align_bytes,
0, 0);
0, 0, false);

LLVMValueRef uncasted_union_ptr;
// Even if safety is off in this block, if the union type has the safety field, we have to populate it
Expand Down Expand Up @@ -5224,7 +5236,7 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f
LLVMPositionBuilderAtEnd(g->builder, ok_block);
LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_payload_index, "");
ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
ZigType *slice_type = get_slice_type(g, u8_ptr_type);
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, payload_ptr, ptr_field_index, "");
Expand Down Expand Up @@ -6470,7 +6482,7 @@ static void generate_error_name_table(CodeGen *g) {
assert(g->errors_by_index.length > 0);

ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
ZigType *str_type = get_slice_type(g, u8_ptr_type);

LLVMValueRef *values = allocate<LLVMValueRef>(g->errors_by_index.length);
Expand Down Expand Up @@ -7551,6 +7563,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" is_volatile: bool,\n"
" alignment: comptime_int,\n"
" child: type,\n"
" is_allowzero: bool,\n"
"\n"
" pub const Size = enum {\n"
" One,\n"
Expand Down Expand Up @@ -8158,7 +8171,7 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
}

ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
ZigType *str_type = get_slice_type(g, u8_ptr_type);
ZigType *fn_type = get_test_fn_type(g);

Expand Down
Loading

0 comments on commit 5eaead6

Please sign in to comment.