diff --git a/doc/docgen.zig b/doc/docgen.zig index c6c7a9954e..63f46ccb6d 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -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(""); diff --git a/doc/langref.html.in b/doc/langref.html.in index eef6e3e516..a561ebc215 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -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#}

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#}. @@ -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#} +

+ 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: +

+ {#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#} @@ -6635,9 +6653,13 @@ fn add(a: i32, b: i32) i32 { return a + b; } {#header_close#} {#header_open|@intToPtr#} -
{#syntax#}@intToPtr(comptime DestType: type, int: usize) DestType{#endsyntax#}
+
{#syntax#}@intToPtr(comptime DestType: type, address: usize) DestType{#endsyntax#}

- Converts an integer to a pointer. To convert the other way, use {#link|@ptrToInt#}. + Converts an integer to a {#link|pointer|Pointers#}. 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#}.

{#header_close#} @@ -8128,6 +8150,11 @@ fn bar(f: *Foo) void { {#header_close#} {#header_open|Pointer Cast Invalid Null#} +

+ 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. +

At compile-time:

{#code_begin|test_err|null pointer casted to type#} comptime { @@ -8988,7 +9015,7 @@ Available libcs: {#header_close#} {#header_open|Style Guide#} @@ -9412,8 +9439,8 @@ PrefixOp PrefixTypeOp <- 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 <- LBRACKET Expr (DOT2 Expr?)? RBRACKET @@ -9564,6 +9591,7 @@ TILDE <- '~' skip end_of_word <- ![a-zA-Z0-9_] skip KEYWORD_align <- 'align' end_of_word +KEYWORD_allowzero <- 'allowzero' end_of_word KEYWORD_and <- 'and' end_of_word KEYWORD_anyerror <- 'anyerror' end_of_word KEYWORD_asm <- 'asm' end_of_word @@ -9614,7 +9642,7 @@ KEYWORD_var <- 'var' end_of_word KEYWORD_volatile <- 'volatile' end_of_word KEYWORD_while <- 'while' end_of_word -keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm +keyword <- 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 diff --git a/src/all_types.hpp b/src/all_types.hpp index 92adac094d..f6e0b2828c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2668,7 +2668,7 @@ struct IrInstructionPtrType { PtrLen ptr_len; bool is_const; bool is_volatile; - bool allow_zero; + bool is_allow_zero; }; struct IrInstructionPromiseType { @@ -2684,7 +2684,7 @@ struct IrInstructionSliceType { IrInstruction *child_type; bool is_const; bool is_volatile; - bool allow_zero; + bool is_allow_zero; }; struct IrInstructionGlobalAsm { diff --git a/src/analyze.cpp b/src/analyze.cpp index f54da2a16b..1e0ddd8451 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -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); @@ -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 { @@ -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) { @@ -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); @@ -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; @@ -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)) @@ -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; @@ -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); @@ -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; diff --git a/src/analyze.hpp b/src/analyze.hpp index 3b4f6b7118..85d698358f 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -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); diff --git a/src/codegen.cpp b/src/codegen.cpp index c9ceb5ee7d..015680b5d2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -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)); } @@ -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, @@ -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, ""); } @@ -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)) { @@ -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; @@ -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); } @@ -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 @@ -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, ""); @@ -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(g->errors_by_index.length); @@ -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" @@ -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); diff --git a/src/ir.cpp b/src/ir.cpp index 46fb86e33f..3851103c1e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1348,7 +1348,7 @@ static IrInstruction *ir_build_br(IrBuilder *irb, Scope *scope, AstNode *source_ static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, - IrInstruction *align_value, uint32_t bit_offset_start, uint32_t host_int_bytes) + IrInstruction *align_value, uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero) { IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); ptr_type_of_instruction->align_value = align_value; @@ -1358,6 +1358,7 @@ static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *s ptr_type_of_instruction->ptr_len = ptr_len; ptr_type_of_instruction->bit_offset_start = bit_offset_start; ptr_type_of_instruction->host_int_bytes = host_int_bytes; + ptr_type_of_instruction->is_allow_zero = is_allow_zero; if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); @@ -1627,13 +1628,14 @@ static IrInstruction *ir_build_promise_type(IrBuilder *irb, Scope *scope, AstNod } static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value) + IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, bool is_allow_zero) { IrInstructionSliceType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_const = is_const; instruction->is_volatile = is_volatile; instruction->child_type = child_type; instruction->align_value = align_value; + instruction->is_allow_zero = is_allow_zero; ir_ref_instruction(child_type, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); @@ -5192,6 +5194,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id); bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; + bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr; AstNode *expr_node = node->data.pointer_type.op_expr; AstNode *align_expr = node->data.pointer_type.align_expr; @@ -5239,7 +5242,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode } return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, - ptr_len, align_value, bit_offset_start, host_int_bytes); + ptr_len, align_value, bit_offset_start, host_int_bytes, is_allow_zero); } static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, @@ -5826,6 +5829,7 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n AstNode *child_type_node = node->data.array_type.child_type; bool is_const = node->data.array_type.is_const; bool is_volatile = node->data.array_type.is_volatile; + bool is_allow_zero = node->data.array_type.allow_zero_token != nullptr; AstNode *align_expr = node->data.array_type.align_expr; Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); @@ -5838,6 +5842,10 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type")); return irb->codegen->invalid_instruction; } + if (is_allow_zero) { + add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type")); + return irb->codegen->invalid_instruction; + } if (align_expr != nullptr) { add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type")); return irb->codegen->invalid_instruction; @@ -5866,7 +5874,7 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n if (child_type == irb->codegen->invalid_instruction) return child_type; - return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, align_value); + return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, align_value, is_allow_zero); } } @@ -7760,7 +7768,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec if (type_has_bits(return_type)) { IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, 0, 0, 0)); + false, false, PtrLenUnknown, 0, 0, 0, false)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, result_ptr, false); @@ -7814,7 +7822,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, 0, 0, 0)); + false, false, PtrLenUnknown, 0, 0, 0, false)); IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe, false); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); @@ -9818,7 +9826,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT ZigType *ptr_type = get_pointer_to_type_extra( ira->codegen, prev_inst->value.type->data.array.child_type, true, false, PtrLenUnknown, - 0, 0, 0); + 0, 0, 0, false); ZigType *slice_type = get_slice_type(ira->codegen, ptr_type); if (err_set_type != nullptr) { return get_error_union_type(ira->codegen, err_set_type, slice_type); @@ -10243,7 +10251,7 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, - ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0); + ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0, false); IrInstruction *const_instr = ir_const(ira, instruction, ptr_type); ConstExprValue *const_val = &const_instr->value; const_val->data.x_ptr.special = ConstPtrSpecialRef; @@ -10576,7 +10584,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, - is_const, is_volatile, PtrLenSingle, 0, 0, 0); + is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; @@ -11983,7 +11991,7 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { return nullptr; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, 0, 0, 0); + true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, ptr_type); IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type); if (type_is_invalid(casted_value->value.type)) @@ -13154,7 +13162,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i out_array_val = out_val; } else if (is_slice(op1_type) || is_slice(op2_type)) { ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, - true, false, PtrLenUnknown, 0, 0, 0); + true, false, PtrLenUnknown, 0, 0, 0, false); result->value.type = get_slice_type(ira->codegen, ptr_type); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; @@ -13175,7 +13183,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i new_len += 1; // null byte // TODO make this `[*]null T` instead of `[*]T` - result->value.type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0); + result->value.type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0, false); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; @@ -14033,7 +14041,7 @@ no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type, - var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); + var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0, false); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; @@ -14328,7 +14336,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *casted_new_stack = nullptr; if (call_instruction->new_stack != nullptr) { ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, 0, 0, 0); + false, false, PtrLenUnknown, 0, 0, 0, false); ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); IrInstruction *new_stack = call_instruction->new_stack->child; if (type_is_invalid(new_stack->value.type)) @@ -15262,7 +15270,8 @@ static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_ali ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_type->data.pointer.ptr_len, new_align, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + ptr_type->data.pointer.allow_zero); } static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { @@ -15279,7 +15288,8 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_len, ptr_type->data.pointer.explicit_alignment, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + ptr_type->data.pointer.allow_zero); } static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { @@ -15330,7 +15340,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, - ptr_type->data.pointer.explicit_alignment, 0, 0); + ptr_type->data.pointer.explicit_alignment, 0, 0, false); } else { uint64_t elem_val_scalar; if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar)) @@ -15342,7 +15352,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, - 1, (uint32_t)bit_offset, ptr_type->data.pointer.host_int_bytes); + 1, (uint32_t)bit_offset, ptr_type->data.pointer.host_int_bytes, false); } } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { @@ -15693,7 +15703,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), - (uint32_t)host_int_bytes_for_result_type); + (uint32_t)host_int_bytes_for_result_type, false); IrInstruction *result = ir_const(ira, source_instr, ptr_type); ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; @@ -15709,7 +15719,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), - host_int_bytes_for_result_type); + host_int_bytes_for_result_type, false); return result; } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, @@ -15748,7 +15758,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ ZigType *field_type = field->type_entry; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, - is_const, is_volatile, PtrLenSingle, 0, 0, 0); + is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); IrInstruction *result = ir_const(ira, source_instr, ptr_type); ConstExprValue *const_val = &result->value; @@ -15761,7 +15771,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, - PtrLenSingle, 0, 0, 0); + PtrLenSingle, 0, 0, 0, false); return result; } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, @@ -16480,6 +16490,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, bool is_const = slice_type_instruction->is_const; bool is_volatile = slice_type_instruction->is_volatile; + bool is_allow_zero = slice_type_instruction->is_allow_zero; switch (child_type->id) { case ZigTypeIdInvalid: // handled above @@ -16516,7 +16527,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, - is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0); + is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0, is_allow_zero); ZigType *result_type = get_slice_type(ira->codegen, slice_ptr_type); return ir_const_type(ira, &slice_type_instruction->base, result_type); } @@ -16749,7 +16760,7 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false); if (instr_is_comptime(base_ptr)) { ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); @@ -17668,7 +17679,7 @@ static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, 0, 0, 0); + true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (casted_value->value.special == ConstValSpecialStatic) { ErrorTableEntry *err = casted_value->value.data.x_err_set; @@ -17713,7 +17724,7 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns ZigType *u8_ptr_type = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, - 0, 0, 0); + 0, 0, 0, false); result->value.type = get_slice_type(ira->codegen, u8_ptr_type); return result; } @@ -17767,7 +17778,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, field_ptr->value.type->data.pointer.is_const, field_ptr->value.type->data.pointer.is_volatile, PtrLenSingle, - field_ptr_align, 0, 0); + field_ptr_align, 0, 0, false); IrInstruction *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type); if (type_is_invalid(casted_field_ptr->value.type)) return ira->codegen->invalid_instruction; @@ -17776,7 +17787,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, casted_field_ptr->value.type->data.pointer.is_const, casted_field_ptr->value.type->data.pointer.is_volatile, PtrLenSingle, - parent_ptr_align, 0, 0); + parent_ptr_align, 0, 0, false); if (instr_is_comptime(casted_field_ptr)) { ConstExprValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad); @@ -18112,7 +18123,7 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, IrInstruction *source_instr, ZigType *u8_ptr = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, - 0, 0, 0); + 0, 0, 0, false); fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_optional = create_const_vals(1); @@ -18216,7 +18227,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty result->special = ConstValSpecialStatic; result->type = type_info_pointer_type; - ConstExprValue *fields = create_const_vals(5); + ConstExprValue *fields = create_const_vals(6); result->data.x_struct.fields = fields; // size: Size @@ -18247,6 +18258,11 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty fields[4].special = ConstValSpecialStatic; fields[4].type = ira->codegen->builtin_types.entry_type; fields[4].data.x_type = attrs_type->data.pointer.child_type; + // is_allowzero: bool + ensure_field_index(result->type, "is_allowzero", 5); + fields[5].special = ConstValSpecialStatic; + fields[5].type = ira->codegen->builtin_types.entry_bool; + fields[5].data.x_bool = attrs_type->data.pointer.allow_zero; return result; }; @@ -19473,12 +19489,12 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_child_type, src_ptr_const, src_ptr_volatile, PtrLenUnknown, - src_ptr_align, 0, 0); + src_ptr_align, 0, 0, false); ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, src_ptr_const, src_ptr_volatile, PtrLenUnknown, - src_ptr_align, 0, 0); + src_ptr_align, 0, 0, false); ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); IrInstruction *casted_value = ir_implicit_cast(ira, target, u8_slice); @@ -19545,7 +19561,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - alignment, 0, 0); + alignment, 0, 0, false); ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); if (instr_is_comptime(target)) { @@ -19773,7 +19789,7 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio dest_align = get_abi_alignment(ira->codegen, u8); } ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, - PtrLenUnknown, dest_align, 0, 0); + PtrLenUnknown, dest_align, 0, 0, false); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); if (type_is_invalid(casted_dest_ptr->value.type)) @@ -19895,9 +19911,9 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio ZigType *usize = ira->codegen->builtin_types.entry_usize; ZigType *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, - PtrLenUnknown, dest_align, 0, 0); + PtrLenUnknown, dest_align, 0, 0, false); ZigType *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, - PtrLenUnknown, src_align, 0, 0); + PtrLenUnknown, src_align, 0, 0, false); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); if (type_is_invalid(casted_dest_ptr->value.type)) @@ -20061,7 +20077,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction ptr_ptr_type->data.pointer.is_const || is_comptime_const, ptr_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - ptr_ptr_type->data.pointer.explicit_alignment, 0, 0); + ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { @@ -20071,7 +20087,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction main_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, - array_type->data.pointer.explicit_alignment, 0, 0); + array_type->data.pointer.explicit_alignment, 0, 0, false); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); @@ -20586,7 +20602,7 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type, false, result_ptr->value.type->data.pointer.is_volatile, PtrLenSingle, - alignment, 0, 0); + alignment, 0, 0, false); } else { expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false); } @@ -20753,7 +20769,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0); + PtrLenSingle, 0, 0, 0, false); if (instr_is_comptime(value)) { ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); if (!ptr_val) @@ -21125,7 +21141,7 @@ static IrInstruction *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstruction } ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, 0, 0, 0); + true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type); if (type_is_invalid(casted_msg->value.type)) @@ -21794,10 +21810,17 @@ static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; + uint64_t addr = bigint_as_unsigned(&val->data.x_bigint); + if (!ptr_allows_addr_zero(ptr_type) && addr == 0) { + ir_add_error(ira, source_instr, + buf_sprintf("pointer type '%s' does not allow address zero", buf_ptr(&ptr_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_const(ira, source_instr, ptr_type); result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; - result->value.data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint); + result->value.data.x_ptr.data.hard_coded_addr.addr = addr; return result; } @@ -21951,6 +21974,9 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct } else if (child_type->id == ZigTypeIdOpaque) { ir_add_error(ira, &instruction->base, buf_sprintf("C pointers cannot point opaque types")); return ira->codegen->invalid_instruction; + } else if (instruction->is_allow_zero) { + ir_add_error(ira, &instruction->base, buf_sprintf("C pointers always allow address zero")); + return ira->codegen->invalid_instruction; } } @@ -21969,10 +21995,12 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct align_bytes = 0; } + bool allow_zero = instruction->is_allow_zero || instruction->ptr_len == PtrLenC; + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, instruction->is_const, instruction->is_volatile, instruction->ptr_len, align_bytes, - instruction->bit_offset_start, instruction->host_int_bytes); + instruction->bit_offset_start, instruction->host_int_bytes, allow_zero); return ir_const_type(ira, &instruction->base, result_type); } diff --git a/src/parser.cpp b/src/parser.cpp index a2c17f401f..9172e21b92 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2530,6 +2530,12 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { if (array != nullptr) { assert(array->type == NodeTypeArrayType); while (true) { + Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); + if (allowzero_token != nullptr) { + array->data.array_type.allow_zero_token = allowzero_token; + continue; + } + AstNode *align_expr = ast_parse_byte_align(pc); if (align_expr != nullptr) { array->data.array_type.align_expr = align_expr; @@ -2545,7 +2551,6 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { array->data.array_type.is_volatile = true; continue; } - break; } @@ -2560,6 +2565,12 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { if (child == nullptr) child = ptr; while (true) { + Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); + if (allowzero_token != nullptr) { + child->data.pointer_type.allow_zero_token = allowzero_token; + continue; + } + if (eat_token_if(pc, TokenIdKeywordAlign) != nullptr) { expect_token(pc, TokenIdLParen); AstNode *align_expr = ast_parse_expr(pc); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 7d41343e3a..53554d1096 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -107,6 +107,7 @@ struct ZigKeyword { static const struct ZigKeyword zig_keywords[] = { {"align", TokenIdKeywordAlign}, + {"allowzero", TokenIdKeywordAllowZero}, {"and", TokenIdKeywordAnd}, {"anyerror", TokenIdKeywordAnyerror}, {"asm", TokenIdKeywordAsm}, @@ -1495,6 +1496,7 @@ const char * token_name(TokenId id) { case TokenIdIntLiteral: return "IntLiteral"; case TokenIdKeywordAsync: return "async"; case TokenIdKeywordAnyerror: return "anyerror"; + case TokenIdKeywordAllowZero: return "allowzero"; case TokenIdKeywordAwait: return "await"; case TokenIdKeywordResume: return "resume"; case TokenIdKeywordSuspend: return "suspend"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 13ab0352d9..f898ca4e59 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -50,6 +50,7 @@ enum TokenId { TokenIdFloatLiteral, TokenIdIntLiteral, TokenIdKeywordAlign, + TokenIdKeywordAllowZero, TokenIdKeywordAnd, TokenIdKeywordAnyerror, TokenIdKeywordAsm, @@ -73,6 +74,7 @@ enum TokenId { TokenIdKeywordFor, TokenIdKeywordIf, TokenIdKeywordInline, + TokenIdKeywordLinkSection, TokenIdKeywordNakedCC, TokenIdKeywordNoAlias, TokenIdKeywordNull, @@ -83,7 +85,6 @@ enum TokenId { TokenIdKeywordPub, TokenIdKeywordResume, TokenIdKeywordReturn, - TokenIdKeywordLinkSection, TokenIdKeywordStdcallCC, TokenIdKeywordStruct, TokenIdKeywordSuspend, diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 2560c47e80..7149d4b3e6 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -125,6 +125,7 @@ pub const Error = union(enum) { ExtraAlignQualifier: ExtraAlignQualifier, ExtraConstQualifier: ExtraConstQualifier, ExtraVolatileQualifier: ExtraVolatileQualifier, + ExtraAllowZeroQualifier: ExtraAllowZeroQualifier, ExpectedPrimaryExpr: ExpectedPrimaryExpr, ExpectedToken: ExpectedToken, ExpectedCommaOrEnd: ExpectedCommaOrEnd, @@ -149,6 +150,7 @@ pub const Error = union(enum) { @TagType(Error).ExtraAlignQualifier => |*x| return x.render(tokens, stream), @TagType(Error).ExtraConstQualifier => |*x| return x.render(tokens, stream), @TagType(Error).ExtraVolatileQualifier => |*x| return x.render(tokens, stream), + @TagType(Error).ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream), @TagType(Error).ExpectedPrimaryExpr => |*x| return x.render(tokens, stream), @TagType(Error).ExpectedToken => |*x| return x.render(tokens, stream), @TagType(Error).ExpectedCommaOrEnd => |*x| return x.render(tokens, stream), @@ -175,6 +177,7 @@ pub const Error = union(enum) { @TagType(Error).ExtraAlignQualifier => |x| return x.token, @TagType(Error).ExtraConstQualifier => |x| return x.token, @TagType(Error).ExtraVolatileQualifier => |x| return x.token, + @TagType(Error).ExtraAllowZeroQualifier => |x| return x.token, @TagType(Error).ExpectedPrimaryExpr => |x| return x.token, @TagType(Error).ExpectedToken => |x| return x.token, @TagType(Error).ExpectedCommaOrEnd => |x| return x.token, @@ -198,6 +201,7 @@ pub const Error = union(enum) { pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); + pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier"); pub const ExpectedCall = struct { node: *Node, @@ -1540,6 +1544,7 @@ pub const Node = struct { }; pub const PtrInfo = struct { + allowzero_token: ?TokenIndex, align_info: ?Align, const_token: ?TokenIndex, volatile_token: ?TokenIndex, diff --git a/std/zig/parse.zig b/std/zig/parse.zig index fe946fb802..93d15975fb 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -1688,6 +1688,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .align_info = null, .const_token = null, .volatile_token = null, + .allowzero_token = null, }, }; stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; @@ -1743,6 +1744,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { addr_of_info.volatile_token = token_index; continue; }, + Token.Id.Keyword_allowzero => { + stack.append(state) catch unreachable; + if (addr_of_info.allowzero_token != null) { + ((try tree.errors.addOne())).* = Error{ .ExtraAllowZeroQualifier = Error.ExtraAllowZeroQualifier{ .token = token_index } }; + return tree; + } + addr_of_info.allowzero_token = token_index; + continue; + }, else => { prevToken(&tok_it, &tree); continue; @@ -3552,6 +3562,7 @@ fn tokenIdToPrefixOp(id: Token.Id) ?ast.Node.PrefixOp.Op { .align_info = null, .const_token = null, .volatile_token = null, + .allowzero_token = null, }, }, Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .OptionalType = void{} }, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 8ffad3ab77..bc50b8ab87 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,10 @@ +test "zig fmt: allowzero pointer" { + try testCanonical( + \\const T = [*]allowzero const u8; + \\ + ); +} + test "zig fmt: enum literal" { try testCanonical( \\const x = .hi; diff --git a/std/zig/render.zig b/std/zig/render.zig index fe14c2ef8c..de4a2cd365 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -379,6 +379,9 @@ fn renderExpression( else => usize(0), }; try renderTokenOffset(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None, star_offset); // * + if (ptr_info.allowzero_token) |allowzero_token| { + try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero + } if (ptr_info.align_info) |align_info| { const lparen_token = tree.prevToken(align_info.node.firstToken()); const align_token = tree.prevToken(lparen_token); @@ -416,6 +419,9 @@ fn renderExpression( try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [ try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ] + if (ptr_info.allowzero_token) |allowzero_token| { + try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero + } if (ptr_info.align_info) |align_info| { const lparen_token = tree.prevToken(align_info.node.firstToken()); const align_token = tree.prevToken(lparen_token); diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 19d64514a1..2ace430a15 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -13,6 +13,7 @@ pub const Token = struct { pub const keywords = []Keyword{ Keyword{ .bytes = "align", .id = Id.Keyword_align }, + Keyword{ .bytes = "allowzero", .id = Id.Keyword_allowzero }, Keyword{ .bytes = "and", .id = Id.Keyword_and }, Keyword{ .bytes = "anyerror", .id = Id.Keyword_anyerror }, Keyword{ .bytes = "asm", .id = Id.Keyword_asm }, @@ -143,6 +144,7 @@ pub const Token = struct { BracketStarCBracket, ShebangLine, Keyword_align, + Keyword_allowzero, Keyword_and, Keyword_anyerror, Keyword_asm, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a193095b0f..edfb73fc42 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@ptrToInt 0 to non optional pointer", + \\export fn entry() void { + \\ var b = @intToPtr(*i32, 0); + \\} + , + "tmp.zig:2:13: error: pointer type '*i32' does not allow address zero", + ); + cases.add( "cast enum literal to enum but it doesn't match", \\const Foo = enum { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 2d153ccff0..78b45ac05f 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,16 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { + cases.addRuntimeSafety("@ptrToInt address zero to non-optional pointer", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ var zero: usize = 0; + \\ var b = @intToPtr(*i32, zero); + \\} + ); + cases.addRuntimeSafety("pointer casting null to non-optional pointer", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig index 5b73cd98df..be47718358 100644 --- a/test/stage1/behavior/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -137,3 +137,16 @@ test "compare equality of optional and non-optional pointer" { expect(a == b); expect(b == a); } + +test "allowzero pointer and slice" { + var ptr = @intToPtr([*]allowzero i32, 0); + var opt_ptr: ?[*]allowzero i32 = ptr; + expect(opt_ptr != null); + expect(@ptrToInt(ptr) == 0); + var slice = ptr[0..10]; + expect(@typeOf(slice) == []allowzero i32); + expect(@ptrToInt(&slice[5]) == 20); + + expect(@typeInfo(@typeOf(ptr)).Pointer.is_allowzero); + expect(@typeInfo(@typeOf(slice)).Pointer.is_allowzero); +}