Commit 5eaead6a56
Changed files (18)
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("<span class=\"tok-kw\">");
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#}
<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#}.
@@ -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#}
@@ -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#}
@@ -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 {
@@ -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#}
@@ -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
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 {
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;
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);
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<LLVMValueRef>(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);
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<IrInstructionPtrType>(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<IrInstructionSliceType>(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);
}
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);
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";
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,
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,
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{} },
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;
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);
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,
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);
+}
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 {
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);