Skip to content

Commit 5eaead6

Browse files
committed
implement allowzero pointer attribute
closes #1953 only needed for freestanding targets. also adds safety for `@intToPtr` when the address is zero.
1 parent 3306e43 commit 5eaead6

18 files changed

+225
-78
lines changed

doc/docgen.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
779779
std.zig.Token.Id.Keyword_use,
780780
std.zig.Token.Id.Keyword_var,
781781
std.zig.Token.Id.Keyword_volatile,
782+
std.zig.Token.Id.Keyword_allowzero,
782783
std.zig.Token.Id.Keyword_while,
783784
=> {
784785
try out.write("<span class=\"tok-kw\">");

doc/langref.html.in

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,7 @@ test "comptime @intToPtr" {
17211721
}
17221722
}
17231723
{#code_end#}
1724-
{#see_also|Optional Pointers|@intToPtr|@ptrToInt#}
1724+
{#see_also|Optional Pointers|@intToPtr|@ptrToInt|C Pointers|Pointers to Zero Bit Types#}
17251725
{#header_open|volatile#}
17261726
<p>Loads and stores are assumed to not have side effects. If a given load or store
17271727
should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}.
@@ -1850,7 +1850,25 @@ fn foo(bytes: []u8) u32 {
18501850
}
18511851
{#code_end#}
18521852
{#header_close#}
1853-
{#see_also|C Pointers|Pointers to Zero Bit Types#}
1853+
1854+
{#header_open|allowzero#}
1855+
<p>
1856+
This pointer attribute allows a pointer to have address zero. This is only ever needed on the
1857+
freestanding OS target, where the address zero is mappable. In this code example, if the pointer
1858+
did not have the {#syntax#}allowzero{#endsyntax#} attribute, this would be a
1859+
{#link|Pointer Cast Invalid Null#} panic:
1860+
</p>
1861+
{#code_begin|test|allowzero#}
1862+
const std = @import("std");
1863+
const assert = std.debug.assert;
1864+
1865+
test "allowzero" {
1866+
var zero: usize = 0;
1867+
var ptr = @intToPtr(*allowzero i32, zero);
1868+
assert(@ptrToInt(ptr) == 0);
1869+
}
1870+
{#code_end#}
1871+
{#header_close#}
18541872
{#header_close#}
18551873

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

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

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

81308152
{#header_open|Pointer Cast Invalid Null#}
8153+
<p>
8154+
This happens when casting a pointer with the address 0 to a pointer which may not have the address 0.
8155+
For example, {#link|C Pointers#}, {#link|Optional Pointers#}, and {#link|allowzero#} pointers
8156+
allow address zero, but normal {#link|Pointers#} do not.
8157+
</p>
81318158
<p>At compile-time:</p>
81328159
{#code_begin|test_err|null pointer casted to type#}
81338160
comptime {
@@ -8988,7 +9015,7 @@ Available libcs:
89889015
<ul>
89899016
<li>Linux x86_64</li>
89909017
<li>Windows x86_64</li>
8991-
<li>MacOS x86_64</li>
9018+
<li>macOS x86_64</li>
89929019
</ul>
89939020
{#header_close#}
89949021
{#header_open|Style Guide#}
@@ -9412,8 +9439,8 @@ PrefixOp
94129439
PrefixTypeOp
94139440
&lt;- QUESTIONMARK
94149441
/ KEYWORD_promise MINUSRARROW
9415-
/ ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
9416-
/ PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
9442+
/ ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
9443+
/ PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
94179444

94189445
SuffixOp
94199446
&lt;- LBRACKET Expr (DOT2 Expr?)? RBRACKET
@@ -9564,6 +9591,7 @@ TILDE &lt;- '~' skip
95649591

95659592
end_of_word &lt;- ![a-zA-Z0-9_] skip
95669593
KEYWORD_align &lt;- 'align' end_of_word
9594+
KEYWORD_allowzero &lt;- 'allowzero' end_of_word
95679595
KEYWORD_and &lt;- 'and' end_of_word
95689596
KEYWORD_anyerror &lt;- 'anyerror' end_of_word
95699597
KEYWORD_asm &lt;- 'asm' end_of_word
@@ -9614,7 +9642,7 @@ KEYWORD_var &lt;- 'var' end_of_word
96149642
KEYWORD_volatile &lt;- 'volatile' end_of_word
96159643
KEYWORD_while &lt;- 'while' end_of_word
96169644

9617-
keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm
9645+
keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_anyerror / KEYWORD_asm
96189646
/ KEYWORD_async / KEYWORD_await / KEYWORD_break / KEYWORD_cancel
96199647
/ KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue
96209648
/ KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer

src/all_types.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2668,7 +2668,7 @@ struct IrInstructionPtrType {
26682668
PtrLen ptr_len;
26692669
bool is_const;
26702670
bool is_volatile;
2671-
bool allow_zero;
2671+
bool is_allow_zero;
26722672
};
26732673

26742674
struct IrInstructionPromiseType {
@@ -2684,7 +2684,7 @@ struct IrInstructionSliceType {
26842684
IrInstruction *child_type;
26852685
bool is_const;
26862686
bool is_volatile;
2687-
bool allow_zero;
2687+
bool is_allow_zero;
26882688
};
26892689

26902690
struct IrInstructionGlobalAsm {

src/analyze.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -418,11 +418,9 @@ static const char *ptr_len_to_star_str(PtrLen ptr_len) {
418418

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

@@ -505,7 +503,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
505503
bit_offset_in_host != 0 || allow_zero)
506504
{
507505
ZigType *peer_type = get_pointer_to_type_extra(g, child_type, false, false,
508-
PtrLenSingle, 0, 0, host_int_bytes);
506+
PtrLenSingle, 0, 0, host_int_bytes, false);
509507
entry->type_ref = peer_type->type_ref;
510508
entry->di_type = peer_type->di_type;
511509
} else {
@@ -548,7 +546,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
548546
}
549547

550548
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) {
551-
return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0);
549+
return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false);
552550
}
553551

554552
ZigType *get_promise_frame_type(CodeGen *g, ZigType *return_type) {
@@ -857,7 +855,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
857855
ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero)
858856
{
859857
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
860-
PtrLenUnknown, 0, 0, 0);
858+
PtrLenUnknown, 0, 0, 0, false);
861859
ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
862860

863861
slice_type_common_init(g, ptr_type, entry);
@@ -881,10 +879,10 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
881879
{
882880
ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
883881
ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
884-
PtrLenUnknown, 0, 0, 0);
882+
PtrLenUnknown, 0, 0, 0, false);
885883
ZigType *bland_child_slice = get_slice_type(g, bland_child_ptr_type);
886884
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false,
887-
PtrLenUnknown, 0, 0, 0);
885+
PtrLenUnknown, 0, 0, 0, false);
888886
ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
889887

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

14201418
static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
14211419
ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
1422-
PtrLenUnknown, 0, 0, 0);
1420+
PtrLenUnknown, 0, 0, 0, false);
14231421
ZigType *str_type = get_slice_type(g, ptr_type);
14241422
ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr);
14251423
if (type_is_invalid(result_val->type))
@@ -5336,7 +5334,7 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
53365334
const_val->special = ConstValSpecialStatic;
53375335
// TODO make this `[*]null u8` instead of `[*]u8`
53385336
const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
5339-
PtrLenUnknown, 0, 0, 0);
5337+
PtrLenUnknown, 0, 0, 0, false);
53405338
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
53415339
const_val->data.x_ptr.data.base_array.array_val = array_val;
53425340
const_val->data.x_ptr.data.base_array.elem_index = 0;
@@ -5481,7 +5479,7 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr
54815479
assert(array_val->type->id == ZigTypeIdArray);
54825480

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

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

55075505
const_val->special = ConstValSpecialStatic;
55085506
const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false,
5509-
ptr_len, 0, 0, 0);
5507+
ptr_len, 0, 0, 0, false);
55105508
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
55115509
const_val->data.x_ptr.data.base_array.array_val = array_val;
55125510
const_val->data.x_ptr.data.base_array.elem_index = elem_index;

src/analyze.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ void emit_error_notes_for_ref_stack(CodeGen *g, ErrorMsg *msg);
1818
ZigType *new_type_table_entry(ZigTypeId id);
1919
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
2020
ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
21-
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
21+
bool is_volatile, PtrLen ptr_len,
22+
uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
23+
bool allow_zero);
2224
uint64_t type_size(CodeGen *g, ZigType *type_entry);
2325
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
2426
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);

src/codegen.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {
956956
}
957957

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

15161516

15171517
ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
1518-
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
1518+
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
15191519
ZigType *str_type = get_slice_type(g, u8_ptr_type);
15201520
LLVMValueRef global_slice_fields[] = {
15211521
full_buf_ptr,
@@ -3103,6 +3103,18 @@ static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executa
31033103
static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutable *executable, IrInstructionIntToPtr *instruction) {
31043104
ZigType *wanted_type = instruction->base.value.type;
31053105
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
3106+
if (!ptr_allows_addr_zero(wanted_type) && ir_want_runtime_safety(g, &instruction->base)) {
3107+
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(target_val));
3108+
LLVMValueRef is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, target_val, zero, "");
3109+
LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntBad");
3110+
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntOk");
3111+
LLVMBuildCondBr(g->builder, is_zero_bit, bad_block, ok_block);
3112+
3113+
LLVMPositionBuilderAtEnd(g->builder, bad_block);
3114+
gen_safety_crash(g, PanicMsgIdPtrCastNull);
3115+
3116+
LLVMPositionBuilderAtEnd(g->builder, ok_block);
3117+
}
31063118
return LLVMBuildIntToPtr(g->builder, target_val, wanted_type->type_ref, "");
31073119
}
31083120

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

32713283
if (have_init_expr) {
32723284
ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->var_type, false, false,
3273-
PtrLenSingle, var->align_bytes, 0, 0);
3285+
PtrLenSingle, var->align_bytes, 0, 0, false);
32743286
LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value);
32753287
gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val);
32763288
} else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) {
@@ -4160,7 +4172,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
41604172
return enum_type->data.enumeration.name_function;
41614173

41624174
ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false,
4163-
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
4175+
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
41644176
ZigType *u8_slice_type = get_slice_type(g, u8_ptr_type);
41654177
ZigType *tag_int_type = enum_type->data.enumeration.tag_int_type;
41664178

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

49544966
ZigType *ptr_type = get_pointer_to_type_extra(g, type_struct_field->type_entry,
49554967
false, false, PtrLenSingle, field_align_bytes,
4956-
(uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes);
4968+
(uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes, false);
49574969

49584970
gen_assign_raw(g, field_ptr, ptr_type, value);
49594971
}
@@ -4969,7 +4981,7 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I
49694981
uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry);
49704982
ZigType *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry,
49714983
false, false, PtrLenSingle, field_align_bytes,
4972-
0, 0);
4984+
0, 0, false);
49734985

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

64726484
ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
6473-
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
6485+
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
64746486
ZigType *str_type = get_slice_type(g, u8_ptr_type);
64756487

64766488
LLVMValueRef *values = allocate<LLVMValueRef>(g->errors_by_index.length);
@@ -7551,6 +7563,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
75517563
" is_volatile: bool,\n"
75527564
" alignment: comptime_int,\n"
75537565
" child: type,\n"
7566+
" is_allowzero: bool,\n"
75547567
"\n"
75557568
" pub const Size = enum {\n"
75567569
" One,\n"
@@ -8158,7 +8171,7 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
81588171
}
81598172

81608173
ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
8161-
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
8174+
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
81628175
ZigType *str_type = get_slice_type(g, u8_ptr_type);
81638176
ZigType *fn_type = get_test_fn_type(g);
81648177

0 commit comments

Comments
 (0)