implement allowzero pointer attribute

closes #1953

only needed for freestanding targets.

also adds safety for `@intToPtr` when the address is zero.
This commit is contained in:
Andrew Kelley 2019-03-25 12:55:45 -04:00
parent 3306e43984
commit 5eaead6a56
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
18 changed files with 225 additions and 78 deletions

View File

@ -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\">");

View File

@ -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 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#}.
</p>
<p>
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
&lt;- QUESTIONMARK
/ KEYWORD_promise MINUSRARROW
/ ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
/ PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
/ ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
/ PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
SuffixOp
&lt;- LBRACKET Expr (DOT2 Expr?)? RBRACKET
@ -9564,6 +9591,7 @@ TILDE &lt;- '~' skip
end_of_word &lt;- ![a-zA-Z0-9_] skip
KEYWORD_align &lt;- 'align' end_of_word
KEYWORD_allowzero &lt;- 'allowzero' end_of_word
KEYWORD_and &lt;- 'and' end_of_word
KEYWORD_anyerror &lt;- 'anyerror' end_of_word
KEYWORD_asm &lt;- 'asm' end_of_word
@ -9614,7 +9642,7 @@ KEYWORD_var &lt;- 'var' end_of_word
KEYWORD_volatile &lt;- 'volatile' end_of_word
KEYWORD_while &lt;- 'while' end_of_word
keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm
keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_anyerror / KEYWORD_asm
/ KEYWORD_async / KEYWORD_await / KEYWORD_break / KEYWORD_cancel
/ KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue
/ KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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";

View File

@ -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,

View File

@ -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,

View File

@ -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{} },

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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 {

View File

@ -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);

View File

@ -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);
}