diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 56c3426d8b..64fc68e4cc 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -144,7 +144,12 @@ pub const TypeInfo = union(enum) { alignment: comptime_int, child: type, is_allowzero: bool, - is_null_terminated: bool, + /// The type of the sentinel is the element type of the pointer, which is + /// the value of the `child` field in this struct. However there is no way + /// to refer to that type here, so this is a pointer to an opaque value. + /// It will be known at compile-time to be the correct type. Dereferencing + /// this pointer will work at compile-time. + sentinel: ?*const c_void, /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. @@ -161,7 +166,12 @@ pub const TypeInfo = union(enum) { pub const Array = struct { len: comptime_int, child: type, - is_null_terminated: bool, + /// The type of the sentinel is the element type of the array, which is + /// the value of the `child` field in this struct. However there is no way + /// to refer to that type here, so this is a pointer to an opaque value. + /// It will be known at compile-time to be the correct type. Dereferencing + /// this pointer will work at compile-time. + sentinel: ?*const c_void, }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/c.zig b/lib/std/c.zig index 1faf45a489..fac13efc69 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -63,7 +63,7 @@ pub extern "c" fn fclose(stream: *FILE) c_int; pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; -pub extern "c" fn printf(format: [*]null const u8, ...) c_int; +pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; pub extern "c" fn abort() noreturn; pub extern "c" fn exit(code: c_int) noreturn; pub extern "c" fn isatty(fd: fd_t) c_int; diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 160b8e6e5e..f790dd683b 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1409,7 +1409,7 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { const size = @as(usize, @sizeOf(T)); if (comptime !trait.is(builtin.TypeId.Pointer)(B) or - (meta.Child(B) != [size]u8 and meta.Child(B) != [size]null u8)) + (meta.Child(B) != [size]u8 and meta.Child(B) != [size:0]u8)) { @compileError("expected *[N]u8 " ++ ", passed " ++ @typeName(B)); } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index ebe396e145..ce19588722 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1552,7 +1552,7 @@ test "zig fmt: pointer attributes" { \\extern fn f2(s: **align(1) *const *volatile u8) c_int; \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int; \\extern fn f4(s: *align(1) const volatile u8) c_int; - \\extern fn f5(s: [*]null align(1) const volatile u8) c_int; + \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int; \\ ); } @@ -1563,7 +1563,7 @@ test "zig fmt: slice attributes" { \\extern fn f2(s: **align(1) *const *volatile u8) c_int; \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int; \\extern fn f4(s: *align(1) const volatile u8) c_int; - \\extern fn f5(s: [*]null align(1) const volatile u8) c_int; + \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int; \\ ); } @@ -1885,7 +1885,7 @@ test "zig fmt: arrays" { \\ 2, \\ }; \\ const a: [0]u8 = []u8{}; - \\ const x: [4]null u8 = undefined; + \\ const x: [4:0]u8 = undefined; \\} \\ ); diff --git a/src/all_types.hpp b/src/all_types.hpp index 4047a7d4a5..1f785a63f3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -55,7 +55,6 @@ enum PtrLen { PtrLenUnknown, PtrLenSingle, PtrLenC, - PtrLenNull, }; // This one corresponds to the builtin.zig enum. @@ -353,19 +352,20 @@ struct LazyValueSliceType { LazyValue base; IrAnalyze *ira; + IrInstruction *sentinel; // can be null IrInstruction *elem_type; IrInstruction *align_inst; // can be null bool is_const; bool is_volatile; bool is_allowzero; - bool is_null_terminated; }; struct LazyValuePtrType { LazyValue base; IrAnalyze *ira; + IrInstruction *sentinel; // can be null IrInstruction *elem_type; IrInstruction *align_inst; // can be null @@ -821,6 +821,7 @@ struct AstNodePrefixOpExpr { struct AstNodePointerType { Token *star_token; + AstNode *sentinel; AstNode *align_expr; BigInt *bit_offset_start; BigInt *host_int_bytes; @@ -828,21 +829,21 @@ struct AstNodePointerType { Token *allow_zero_token; bool is_const; bool is_volatile; - bool is_null_terminated; }; struct AstNodeInferredArrayType { + AstNode *sentinel; // can be null AstNode *child_type; }; struct AstNodeArrayType { AstNode *size; + AstNode *sentinel; AstNode *child_type; AstNode *align_expr; Token *allow_zero_token; bool is_const; bool is_volatile; - bool is_null_terminated; }; struct AstNodeUsingNamespace { @@ -1208,6 +1209,11 @@ struct ZigTypePointer { // struct. InferredStructField *inferred_struct_field; + // This can be null. If it is non-null, it means the pointer is terminated by this + // sentinel value. This is most commonly used for C-style strings, with a 0 byte + // to specify the length of the memory pointed to. + ConstExprValue *sentinel; + PtrLen ptr_len; uint32_t explicit_alignment; // 0 means use ABI alignment @@ -1235,7 +1241,7 @@ struct ZigTypeFloat { struct ZigTypeArray { ZigType *child_type; uint64_t len; - bool is_null_terminated; + ConstExprValue *sentinel; }; struct TypeStructField { @@ -1761,8 +1767,10 @@ struct TypeId { union { struct { + CodeGen *codegen; ZigType *child_type; InferredStructField *inferred_struct_field; + ConstExprValue *sentinel; PtrLen ptr_len; uint32_t alignment; @@ -1775,9 +1783,10 @@ struct TypeId { bool allow_zero; } pointer; struct { + CodeGen *codegen; ZigType *child_type; uint64_t size; - bool is_null_terminated; + ConstExprValue *sentinel; } array; struct { bool is_signed; @@ -2033,6 +2042,7 @@ struct CodeGen { IrInstruction *invalid_instruction; IrInstruction *unreach_instruction; + ConstExprValue const_zero_byte; ConstExprValue const_void_val; ConstExprValue panic_msg_vals[PanicMsgIdCount]; @@ -2989,13 +2999,14 @@ struct IrInstructionArrayType { IrInstruction base; IrInstruction *size; + IrInstruction *sentinel; IrInstruction *child_type; - bool is_null_terminated; }; struct IrInstructionPtrType { IrInstruction base; + IrInstruction *sentinel; IrInstruction *align_value; IrInstruction *child_type; uint32_t bit_offset_start; @@ -3015,12 +3026,12 @@ struct IrInstructionAnyFrameType { struct IrInstructionSliceType { IrInstruction base; + IrInstruction *sentinel; IrInstruction *align_value; IrInstruction *child_type; bool is_const; bool is_volatile; bool is_allow_zero; - bool is_null_terminated; }; struct IrInstructionGlobalAsm { diff --git a/src/analyze.cpp b/src/analyze.cpp index 9c42177d11..aa58d64aa4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -452,20 +452,6 @@ ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type) { return entry; } -static const char *ptr_len_to_star_str(PtrLen ptr_len) { - switch (ptr_len) { - case PtrLenSingle: - return "*"; - case PtrLenUnknown: - return "[*]"; - case PtrLenC: - return "[*c]"; - case PtrLenNull: - return "[*]null "; - } - zig_unreachable(); -} - ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) { if (fn->frame_type != nullptr) { return fn->frame_type; @@ -485,10 +471,47 @@ ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) { return entry; } +static void append_ptr_type_attrs(Buf *type_name, ZigType *ptr_type) { + const char *const_str = ptr_type->data.pointer.is_const ? "const " : ""; + const char *volatile_str = ptr_type->data.pointer.is_volatile ? "volatile " : ""; + const char *allow_zero_str; + if (ptr_type->data.pointer.ptr_len == PtrLenC) { + assert(ptr_type->data.pointer.allow_zero); + allow_zero_str = ""; + } else { + allow_zero_str = ptr_type->data.pointer.allow_zero ? "allowzero " : ""; + } + if (ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.host_int_bytes != 0 || + ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) + { + buf_appendf(type_name, "align("); + if (ptr_type->data.pointer.explicit_alignment != 0) { + buf_appendf(type_name, "%" PRIu32, ptr_type->data.pointer.explicit_alignment); + } + if (ptr_type->data.pointer.host_int_bytes != 0) { + buf_appendf(type_name, ":%" PRIu32 ":%" PRIu32, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); + } + if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { + buf_appendf(type_name, ":?"); + } else if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { + buf_appendf(type_name, ":%" PRIu32, ptr_type->data.pointer.vector_index); + } + buf_appendf(type_name, ")"); + } + buf_appendf(type_name, "%s%s%s", const_str, volatile_str, allow_zero_str); + if (ptr_type->data.pointer.inferred_struct_field != nullptr) { + buf_appendf(type_name, " field '%s' of %s)", + buf_ptr(ptr_type->data.pointer.inferred_struct_field->field_name), + buf_ptr(&ptr_type->data.pointer.inferred_struct_field->inferred_struct_type->name)); + } else { + buf_appendf(type_name, "%s", buf_ptr(&ptr_type->data.pointer.child_type->name)); + } +} + ZigType *get_pointer_to_type_extra2(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, bool allow_zero, - uint32_t vector_index, InferredStructField *inferred_struct_field) + uint32_t vector_index, InferredStructField *inferred_struct_field, ConstExprValue *sentinel) { assert(ptr_len != PtrLenC || allow_zero); assert(!type_is_invalid(child_type)); @@ -511,9 +534,11 @@ ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_con TypeId type_id = {}; ZigType **parent_pointer = nullptr; if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || - allow_zero || vector_index != VECTOR_INDEX_NONE || inferred_struct_field != nullptr) + allow_zero || vector_index != VECTOR_INDEX_NONE || inferred_struct_field != nullptr || + sentinel != nullptr) { type_id.id = ZigTypeIdPointer; + type_id.data.pointer.codegen = g; type_id.data.pointer.child_type = child_type; type_id.data.pointer.is_const = is_const; type_id.data.pointer.is_volatile = is_volatile; @@ -524,6 +549,7 @@ ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_con type_id.data.pointer.allow_zero = allow_zero; type_id.data.pointer.vector_index = vector_index; type_id.data.pointer.inferred_struct_field = inferred_struct_field; + type_id.data.pointer.sentinel = sentinel; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) @@ -539,56 +565,35 @@ ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_con ZigType *entry = new_type_table_entry(ZigTypeIdPointer); - const char *star_str = ptr_len_to_star_str(ptr_len); - const char *const_str = is_const ? "const " : ""; - const char *volatile_str = is_volatile ? "volatile " : ""; - const char *allow_zero_str; - if (ptr_len == PtrLenC) { - assert(allow_zero); - allow_zero_str = ""; - } else { - allow_zero_str = allow_zero ? "allowzero " : ""; - } buf_resize(&entry->name, 0); - if (host_int_bytes == 0 && byte_alignment == 0 && vector_index == VECTOR_INDEX_NONE) { - if (inferred_struct_field == nullptr) { - buf_appendf(&entry->name, "%s%s%s%s%s", - star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name)); - } else { - buf_appendf(&entry->name, "(%s%s%s%s field '%s' of %s)", - star_str, const_str, volatile_str, allow_zero_str, - buf_ptr(inferred_struct_field->field_name), - buf_ptr(&inferred_struct_field->inferred_struct_type->name)); - } - } else if (host_int_bytes == 0 && vector_index == VECTOR_INDEX_NONE) { - buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment, - const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name)); - } else if (byte_alignment == 0) { - assert(vector_index == VECTOR_INDEX_NONE); - buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s", - star_str, - bit_offset_in_host, host_int_bytes, - const_str, volatile_str, allow_zero_str, - buf_ptr(&child_type->name)); - } else if (vector_index == VECTOR_INDEX_NONE) { - buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s", - star_str, byte_alignment, - bit_offset_in_host, host_int_bytes, - const_str, volatile_str, allow_zero_str, - buf_ptr(&child_type->name)); - } else if (vector_index == VECTOR_INDEX_RUNTIME) { - buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ":?) %s%s%s%s", - star_str, byte_alignment, - bit_offset_in_host, host_int_bytes, - const_str, volatile_str, allow_zero_str, - buf_ptr(&child_type->name)); - } else { - buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s", - star_str, byte_alignment, - bit_offset_in_host, host_int_bytes, vector_index, - const_str, volatile_str, allow_zero_str, - buf_ptr(&child_type->name)); + if (inferred_struct_field != nullptr) { + buf_appendf(&entry->name, "("); } + switch (ptr_len) { + case PtrLenSingle: + buf_appendf(&entry->name, "*"); + break; + case PtrLenUnknown: + buf_appendf(&entry->name, "[*"); + break; + case PtrLenC: + assert(sentinel == nullptr); + buf_appendf(&entry->name, "[*c]"); + break; + } + if (sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, sentinel); + } + switch (ptr_len) { + case PtrLenSingle: + case PtrLenC: + break; + case PtrLenUnknown: + buf_appendf(&entry->name, "]"); + break; + } + if (type_is_resolved(child_type, ResolveStatusZeroBitsKnown)) { if (type_has_bits(child_type)) { @@ -617,6 +622,9 @@ ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_con entry->data.pointer.allow_zero = allow_zero; entry->data.pointer.vector_index = vector_index; entry->data.pointer.inferred_struct_field = inferred_struct_field; + entry->data.pointer.sentinel = sentinel; + + append_ptr_type_attrs(&entry->name, entry); if (parent_pointer) { *parent_pointer = entry; @@ -631,12 +639,12 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero) { return get_pointer_to_type_extra2(g, child_type, is_const, is_volatile, ptr_len, - byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr); + byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr, nullptr); } ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) { return get_pointer_to_type_extra2(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false, - VECTOR_INDEX_NONE, nullptr); + VECTOR_INDEX_NONE, nullptr, nullptr); } ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { @@ -752,12 +760,13 @@ ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payloa return entry; } -ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, bool is_null_terminated) { +ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ConstExprValue *sentinel) { TypeId type_id = {}; type_id.id = ZigTypeIdArray; + type_id.data.array.codegen = g; type_id.data.array.child_type = child_type; type_id.data.array.size = array_size; - type_id.data.array.is_null_terminated = is_null_terminated; + type_id.data.array.sentinel = sentinel; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) { return existing_entry->value; @@ -767,16 +776,19 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, bo ZigType *entry = new_type_table_entry(ZigTypeIdArray); - const char *null_str = is_null_terminated ? "null " : ""; - buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "[%" ZIG_PRI_u64 "]%s%s", array_size, null_str, buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "[%" ZIG_PRI_u64, array_size); + if (sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, sentinel); + } + buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name)); size_t full_array_size; if (array_size == 0) { full_array_size = 0; } else { - full_array_size = array_size + (is_null_terminated ? 1 : 0); + full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0); } entry->size_in_bits = child_type->size_in_bits * full_array_size; @@ -785,7 +797,7 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, bo entry->data.array.child_type = child_type; entry->data.array.len = array_size; - entry->data.array.is_null_terminated = is_null_terminated; + entry->data.array.sentinel = sentinel; g->type_table.put(type_id, entry); return entry; @@ -793,7 +805,7 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, bo ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { assert(ptr_type->id == ZigTypeIdPointer); - assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown || ptr_type->data.pointer.ptr_len == PtrLenNull); + assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown); ZigType **parent_pointer = &ptr_type->data.pointer.slice_parent; if (*parent_pointer) { @@ -802,10 +814,14 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { ZigType *entry = new_type_table_entry(ZigTypeIdStruct); - // replace the & with [] to go from a ptr type name to a slice type name buf_resize(&entry->name, 0); - size_t name_offset = (ptr_type->data.pointer.ptr_len == PtrLenSingle) ? 1 : 3; - buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset); + buf_appendf(&entry->name, "["); + if (ptr_type->data.pointer.sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, ptr_type->data.pointer.sentinel); + } + buf_appendf(&entry->name, "]"); + append_ptr_type_attrs(&entry->name, ptr_type); unsigned element_count = 2; Buf *ptr_field_name = buf_create_from_str("ptr"); @@ -5639,14 +5655,14 @@ void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { // first we build the underlying array ConstExprValue *array_val = create_const_vals(1); array_val->special = ConstValSpecialStatic; - array_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str), true); + array_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str), &g->const_zero_byte); array_val->data.x_array.special = ConstArraySpecialBuf; array_val->data.x_array.data.s_buf = str; // then make the pointer point to it const_val->special = ConstValSpecialStatic; const_val->type = get_pointer_to_type_extra2(g, array_val->type, true, false, - PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr); + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr); const_val->data.x_ptr.special = ConstPtrSpecialRef; const_val->data.x_ptr.data.ref.pointee = array_val; @@ -6079,7 +6095,7 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { fields.append({"@stack_trace", get_stack_trace_type(g), 0}); fields.append({"@instruction_addresses", - get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, false), 0}); + get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0}); } frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name), @@ -6287,7 +6303,7 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { if (codegen_fn_has_err_ret_tracing_stack(g, fn, true)) { fields.append({"@stack_trace", get_stack_trace_type(g), 0}); fields.append({"@instruction_addresses", - get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, false), 0}); + get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0}); } for (size_t alloca_i = 0; alloca_i < fn->alloca_gen_list.length; alloca_i += 1) { @@ -7050,11 +7066,12 @@ uint32_t type_id_hash(TypeId x) { (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) + (((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) + (((uint32_t)x.data.pointer.vector_index) ^ (uint32_t)0x19199716) + - (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881); + (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881) * + (x.data.pointer.sentinel ? hash_const_val(x.data.pointer.sentinel) : (uint32_t)2955491856); case ZigTypeIdArray: return hash_ptr(x.data.array.child_type) * ((uint32_t)x.data.array.size ^ (uint32_t)2122979968) * - ((uint32_t)x.data.array.is_null_terminated ^ (uint32_t)2048352596); + (x.data.array.sentinel ? hash_const_val(x.data.array.sentinel) : (uint32_t)1927201585); case ZigTypeIdInt: return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) + (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557); @@ -7105,6 +7122,11 @@ bool type_id_eql(TypeId a, TypeId b) { a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host && a.data.pointer.vector_index == b.data.pointer.vector_index && a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes && + ( + a.data.pointer.sentinel == b.data.pointer.sentinel || + (a.data.pointer.sentinel != nullptr && b.data.pointer.sentinel != nullptr && + const_values_equal(a.data.pointer.codegen, a.data.pointer.sentinel, b.data.pointer.sentinel)) + ) && ( a.data.pointer.inferred_struct_field == b.data.pointer.inferred_struct_field || (a.data.pointer.inferred_struct_field != nullptr && @@ -7117,7 +7139,11 @@ bool type_id_eql(TypeId a, TypeId b) { case ZigTypeIdArray: return a.data.array.child_type == b.data.array.child_type && a.data.array.size == b.data.array.size && - a.data.array.is_null_terminated == b.data.array.is_null_terminated; + ( + a.data.array.sentinel == b.data.array.sentinel || + (a.data.array.sentinel != nullptr && b.data.array.sentinel != nullptr && + const_values_equal(a.data.array.codegen, a.data.array.sentinel, b.data.array.sentinel)) + ); case ZigTypeIdInt: return a.data.integer.is_signed == b.data.integer.is_signed && a.data.integer.bit_count == b.data.integer.bit_count; @@ -8303,7 +8329,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->type_entry->abi_size; if (padding_bytes > 0) { ZigType *u8_type = get_int_type(g, false, 8); - ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, false); + ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr); LLVMTypeRef union_element_types[] = { most_aligned_union_member->type_entry->llvm_type, get_llvm_type(g, padding_array), @@ -8337,7 +8363,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta union_type_ref = get_llvm_type(g, most_aligned_union_member->type_entry); } else { ZigType *u8_type = get_int_type(g, false, 8); - ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, false); + ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr); LLVMTypeRef union_element_types[] = { get_llvm_type(g, most_aligned_union_member->type_entry), get_llvm_type(g, padding_array), @@ -8418,19 +8444,19 @@ static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type, ResolveStatus if (type->data.pointer.is_const || type->data.pointer.is_volatile || type->data.pointer.explicit_alignment != 0 || type->data.pointer.ptr_len != PtrLenSingle || type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero || - type->data.pointer.vector_index != VECTOR_INDEX_NONE) + type->data.pointer.vector_index != VECTOR_INDEX_NONE || type->data.pointer.sentinel != nullptr) { assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl)); ZigType *peer_type; if (type->data.pointer.vector_index == VECTOR_INDEX_NONE) { peer_type = get_pointer_to_type_extra2(g, elem_type, false, false, PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false, - VECTOR_INDEX_NONE, nullptr); + VECTOR_INDEX_NONE, nullptr, nullptr); } else { uint32_t host_vec_len = type->data.pointer.host_int_bytes; ZigType *host_vec_type = get_vector_type(g, host_vec_len, elem_type); peer_type = get_pointer_to_type_extra2(g, host_vec_type, false, false, - PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr); + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr); } type->llvm_type = get_llvm_type(g, peer_type); type->llvm_di_type = get_llvm_di_type(g, peer_type); @@ -8659,8 +8685,8 @@ static void resolve_llvm_types_array(CodeGen *g, ZigType *type) { ZigType *elem_type = type->data.array.child_type; - uint64_t extra_len_from_null = type->data.array.is_null_terminated ? 1 : 0; - uint64_t full_len = type->data.array.len + extra_len_from_null; + uint64_t extra_len_from_sentinel = (type->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = type->data.array.len + extra_len_from_sentinel; // TODO https://github.com/ziglang/zig/issues/1424 type->llvm_type = LLVMArrayType(get_llvm_type(g, elem_type), (unsigned)full_len); @@ -9166,16 +9192,3 @@ void IrExecutable::src() { it->source_node->src(); } } - -ConstExprValue *get_null_value(ZigType *ty) { - if (ty->id == ZigTypeIdInt || ty->id == ZigTypeIdComptimeInt) { - return create_const_unsigned_negative(ty, 0, false); - } else if (ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdComptimeFloat) { - return create_const_float(ty, NAN); - } else if (ty->id == ZigTypeIdOptional) { - return create_const_null(ty); - } else { - zig_unreachable(); - } -} - diff --git a/src/analyze.hpp b/src/analyze.hpp index a3d4621178..fcf993eac4 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -24,7 +24,8 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, ZigType *get_pointer_to_type_extra2(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 allow_zero, uint32_t vector_index, InferredStructField *inferred_struct_field); + bool allow_zero, uint32_t vector_index, InferredStructField *inferred_struct_field, + ConstExprValue *sentinel); 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); @@ -33,7 +34,7 @@ ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type); ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id); ZigType *get_optional_type(CodeGen *g, ZigType *child_type); -ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, bool is_null_terminated); +ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ConstExprValue *sentinel); ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type); ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout); @@ -276,5 +277,4 @@ IrInstruction *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, Error analyze_import(CodeGen *codegen, ZigType *source_import, Buf *import_target_str, ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path); ConstExprValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry); -ConstExprValue *get_null_value(ZigType *ty); #endif diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 53388fa033..f4f97c3730 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -147,9 +147,9 @@ static const char *token_to_ptr_len_str(Token *tok) { case TokenIdStar: case TokenIdStarStar: return "*"; - case TokenIdBracketStarBracket: + case TokenIdLBracket: return "[*]"; - case TokenIdBracketStarCBracket: + case TokenIdSymbol: return "[*c]"; default: zig_unreachable(); diff --git a/src/codegen.cpp b/src/codegen.cpp index 4fc3b968c1..9a269ec41f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3707,8 +3707,8 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI array_type = array_type->data.pointer.child_type; } if (safety_check_on) { - uint64_t extra_len_from_null = array_type->data.array.is_null_terminated ? 1 : 0; - uint64_t full_len = array_type->data.array.len + extra_len_from_null; + uint64_t extra_len_from_sentinel = (array_type->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = array_type->data.array.len + extra_len_from_sentinel; LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, full_len, false); add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); } @@ -6636,8 +6636,8 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; assert(array_const_val->type->id == ZigTypeIdArray); if (!type_has_bits(array_const_val->type)) { - if (array_const_val->type->data.array.is_null_terminated) { - ConstExprValue *pointee = get_null_value(array_const_val->type->data.array.child_type); + if (array_const_val->type->data.array.sentinel != nullptr) { + ConstExprValue *pointee = array_const_val->type->data.array.sentinel; render_const_val(g, pointee, ""); render_const_val_global(g, pointee, ""); const_val->global_refs->llvm_value = LLVMConstBitCast(pointee->global_refs->llvm_global, @@ -6963,8 +6963,8 @@ check: switch (const_val->special) { case ConstArraySpecialUndef: return LLVMGetUndef(get_llvm_type(g, type_entry)); case ConstArraySpecialNone: { - uint64_t extra_len_from_null = type_entry->data.array.is_null_terminated ? 1 : 0; - uint64_t full_len = len + extra_len_from_null; + uint64_t extra_len_from_sentinel = (type_entry->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = len + extra_len_from_sentinel; LLVMValueRef *values = allocate(full_len); LLVMTypeRef element_type_ref = get_llvm_type(g, type_entry->data.array.child_type); bool make_unnamed_struct = false; @@ -6974,8 +6974,8 @@ check: switch (const_val->special) { values[i] = val; make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, elem_value->type, val); } - if (type_entry->data.array.is_null_terminated) { - values[len] = LLVMConstNull(element_type_ref); + if (type_entry->data.array.sentinel != nullptr) { + values[len] = gen_const_val(g, type_entry->data.array.sentinel, ""); } if (make_unnamed_struct) { return LLVMConstStruct(values, full_len, true); @@ -6986,7 +6986,7 @@ check: switch (const_val->special) { case ConstArraySpecialBuf: { Buf *buf = const_val->data.x_array.data.s_buf; return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), - !type_entry->data.array.is_null_terminated); + type_entry->data.array.sentinel == nullptr); } } zig_unreachable(); @@ -7479,7 +7479,7 @@ static void do_code_gen(CodeGen *g) { !is_async && !have_err_ret_trace_arg; LLVMValueRef err_ret_array_val = nullptr; if (have_err_ret_trace_stack) { - ZigType *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, false); + ZigType *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr); err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type)); (void)get_llvm_type(g, get_stack_trace_type(g)); @@ -8642,6 +8642,11 @@ static void init(CodeGen *g) { g->const_void_val.type = g->builtin_types.entry_void; g->const_void_val.global_refs = allocate(1); + g->const_zero_byte.special = ConstValSpecialStatic; + g->const_zero_byte.type = g->builtin_types.entry_u8; + g->const_zero_byte.global_refs = allocate(1); + bigint_init_unsigned(&g->const_zero_byte.data.x_bigint, 0); + { ConstGlobalRefs *global_refs = allocate(PanicMsgIdCount); for (size_t i = 0; i < PanicMsgIdCount; i += 1) { @@ -9081,7 +9086,7 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { zig_unreachable(); ConstExprValue *test_fn_array = create_const_vals(1); - test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length, false); + test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length, nullptr); test_fn_array->special = ConstValSpecialStatic; test_fn_array->data.x_array.data.s_none.elements = create_const_vals(g->test_fns.length); diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp index 825ae4baa5..98bbcc6a42 100644 --- a/src/dump_analysis.cpp +++ b/src/dump_analysis.cpp @@ -992,10 +992,6 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { jw_object_field(jw, "len"); jw_int(jw, 3); break; - case PtrLenNull: - jw_object_field(jw, "len"); - jw_int(jw, 4); - break; } anal_dump_pointer_attrs(ctx, ty); break; diff --git a/src/ir.cpp b/src/ir.cpp index 03ec3c0436..a7a3fc51e4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -67,9 +67,10 @@ enum ConstCastResultId { ConstCastResultIdAsyncAllocatorType, ConstCastResultIdBadAllowsZero, ConstCastResultIdArrayChild, - ConstCastResultIdBadNullTermArrays, + ConstCastResultIdSentinelArrays, ConstCastResultIdPtrLens, ConstCastResultIdCV, + ConstCastResultIdPtrSentinel, }; struct ConstCastOnly; @@ -94,8 +95,8 @@ struct ConstCastTypeMismatch; struct ConstCastArrayMismatch; struct ConstCastBadAllowsZero; struct ConstCastBadNullTermArrays; -struct ConstCastBadPtrLens; struct ConstCastBadCV; +struct ConstCastPtrSentinel; struct ConstCastOnly { ConstCastResultId id; @@ -113,9 +114,9 @@ struct ConstCastOnly { ConstCastArg fn_arg; ConstCastArgNoAlias arg_no_alias; ConstCastBadAllowsZero *bad_allows_zero; - ConstCastBadNullTermArrays *bad_null_term_arrays; - ConstCastBadPtrLens *bad_ptr_lens; + ConstCastBadNullTermArrays *sentinel_arrays; ConstCastBadCV *bad_cv; + ConstCastPtrSentinel *bad_ptr_sentinel; } data; }; @@ -175,14 +176,13 @@ struct ConstCastBadNullTermArrays { ZigType *actual_type; }; -struct ConstCastBadPtrLens { +struct ConstCastBadCV { ZigType *wanted_type; ZigType *actual_type; }; -struct ConstCastBadCV { +struct ConstCastPtrSentinel { ZigType *wanted_type; - ZigType *actual_type; }; static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); @@ -264,8 +264,7 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialBaseArray: { ConstExprValue *array_val = const_val->data.x_ptr.data.base_array.array_val; if (const_val->data.x_ptr.data.base_array.elem_index == array_val->type->data.array.len) { - assert(array_val->type->data.array.is_null_terminated); - result = get_null_value(array_val->type->data.array.child_type); + result = array_val->type->data.array.sentinel; } else { expand_undef_array(g, array_val); result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; @@ -317,7 +316,7 @@ static bool slice_is_const(ZigType *type) { // This function returns true when you can change the type of a ConstExprValue and the // value remains meaningful. -static bool types_have_same_zig_comptime_repr(ZigType *expected, ZigType *actual) { +static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expected, ZigType *actual) { if (expected == actual) return true; @@ -366,7 +365,8 @@ static bool types_have_same_zig_comptime_repr(ZigType *expected, ZigType *actual case ZigTypeIdArray: return expected->data.array.len == actual->data.array.len && expected->data.array.child_type == actual->data.array.child_type && - (!expected->data.array.is_null_terminated || actual->data.array.is_null_terminated); + (expected->data.array.sentinel == nullptr || (actual->data.array.sentinel != nullptr && + const_values_equal(codegen, expected->data.array.sentinel, actual->data.array.sentinel))); } zig_unreachable(); } @@ -1576,9 +1576,11 @@ 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, bool is_allow_zero) + IrInstruction *sentinel, IrInstruction *align_value, + uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero) { IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); + ptr_type_of_instruction->sentinel = sentinel; ptr_type_of_instruction->align_value = align_value; ptr_type_of_instruction->child_type = child_type; ptr_type_of_instruction->is_const = is_const; @@ -1588,6 +1590,7 @@ static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *s ptr_type_of_instruction->host_int_bytes = host_int_bytes; ptr_type_of_instruction->is_allow_zero = is_allow_zero; + if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); @@ -1804,14 +1807,15 @@ static IrInstruction *ir_build_set_float_mode(IrBuilder *irb, Scope *scope, AstN } static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *size, - IrInstruction *child_type, bool is_null_terminated) + IrInstruction *sentinel, IrInstruction *child_type) { IrInstructionArrayType *instruction = ir_build_instruction(irb, scope, source_node); instruction->size = size; + instruction->sentinel = sentinel; instruction->child_type = child_type; - instruction->is_null_terminated = is_null_terminated; ir_ref_instruction(size, irb->current_basic_block); + if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); return &instruction->base; @@ -1827,20 +1831,22 @@ static IrInstruction *ir_build_anyframe_type(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } + 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, bool is_allow_zero, - bool is_null_terminated) + IrInstruction *child_type, bool is_const, bool is_volatile, + IrInstruction *sentinel, IrInstruction *align_value, bool is_allow_zero) { IrInstructionSliceType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_const = is_const; instruction->is_volatile = is_volatile; instruction->child_type = child_type; + instruction->sentinel = sentinel; instruction->align_value = align_value; instruction->is_allow_zero = is_allow_zero; - instruction->is_null_terminated = is_null_terminated; + if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); - if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); return &instruction->base; } @@ -6067,9 +6073,9 @@ static PtrLen star_token_to_ptr_len(TokenId token_id) { case TokenIdStar: case TokenIdStarStar: return PtrLenSingle; - case TokenIdBracketStarBracket: + case TokenIdLBracket: return PtrLenUnknown; - case TokenIdBracketStarCBracket: + case TokenIdSymbol: return PtrLenC; default: zig_unreachable(); @@ -6080,22 +6086,22 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode assert(node->type == NodeTypePointerType); PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id); - if (node->data.pointer_type.is_null_terminated) { - if (ptr_len == PtrLenUnknown) { - ptr_len = PtrLenNull; - } else { - exec_add_error_node(irb->codegen, irb->exec, node, - buf_sprintf("null-terminated pointer must be specified with [*] token")); - return irb->codegen->invalid_instruction; - } - } 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 *sentinel_expr = node->data.pointer_type.sentinel; AstNode *expr_node = node->data.pointer_type.op_expr; AstNode *align_expr = node->data.pointer_type.align_expr; + IrInstruction *sentinel; + if (sentinel_expr != nullptr) { + sentinel = ir_gen_node(irb, sentinel_expr, scope); + if (sentinel == irb->codegen->invalid_instruction) + return sentinel; + } else { + sentinel = nullptr; + } IrInstruction *align_value; if (align_expr != nullptr) { @@ -6141,7 +6147,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, is_allow_zero); + ptr_len, sentinel, align_value, bit_offset_start, host_int_bytes, is_allow_zero); } static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, @@ -6245,13 +6251,22 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A buf_sprintf("initializing array with struct syntax")); return irb->codegen->invalid_instruction; } + IrInstruction *sentinel; + if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) { + sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope); + if (sentinel == irb->codegen->invalid_instruction) + return sentinel; + } else { + sentinel = nullptr; + } + IrInstruction *elem_type = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.child_type, scope); if (elem_type == irb->codegen->invalid_instruction) return elem_type; size_t item_count = container_init_expr->entries.length; IrInstruction *item_count_inst = ir_build_const_usize(irb, scope, node, item_count); - container_type = ir_build_array_type(irb, scope, node, item_count_inst, elem_type, false); + container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type); } else { container_type = ir_gen_node(irb, container_init_expr->type, scope); if (container_type == irb->codegen->invalid_instruction) @@ -6975,10 +6990,20 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n 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; - bool is_null_terminated = node->data.array_type.is_null_terminated; + AstNode *sentinel_expr = node->data.array_type.sentinel; AstNode *align_expr = node->data.array_type.align_expr; Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); + + IrInstruction *sentinel; + if (sentinel_expr != nullptr) { + sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope); + if (sentinel == irb->codegen->invalid_instruction) + return sentinel; + } else { + sentinel = nullptr; + } + if (size_node) { if (is_const) { add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type")); @@ -7005,7 +7030,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_array_type(irb, scope, node, size_value, child_type, is_null_terminated); + return ir_build_array_type(irb, scope, node, size_value, sentinel, child_type); } else { IrInstruction *align_value; if (align_expr != nullptr) { @@ -7020,8 +7045,8 @@ 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, is_allow_zero, - is_null_terminated); + return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, sentinel, + align_value, is_allow_zero); } } @@ -8698,7 +8723,7 @@ ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprVal case OnePossibleValueYes: return get_the_one_possible_value(codegen, expected_type); } - if (!types_have_same_zig_comptime_repr(expected_type, val->type)) { + if (!types_have_same_zig_comptime_repr(codegen, expected_type, val->type)) { if ((err = eval_comptime_ptr_reinterpret(ira, codegen, source_node, const_val))) return nullptr; return const_ptr_pointee_unchecked(codegen, const_val); @@ -9846,7 +9871,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted // alignment can be decreased // bit offset attributes must match exactly // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one - // PtrLenNull can coerce into PtrLenUnknown + // sentinel-terminated pointers can coerce into PtrLenUnknown ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type); ZigType *actual_ptr_type = get_src_ptr_type(actual_type); bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); @@ -9858,15 +9883,20 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted bool actual_opt_or_ptr = actual_ptr_type != nullptr && (actual_type->id == ZigTypeIdPointer || actual_type->id == ZigTypeIdOptional); if (wanted_opt_or_ptr && actual_opt_or_ptr) { - bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len; bool ok_null_term_ptrs = - actual_ptr_type->data.pointer.ptr_len == PtrLenNull || - wanted_ptr_type->data.pointer.ptr_len == PtrLenUnknown; - if (!(ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr || ok_null_term_ptrs)) { + wanted_ptr_type->data.pointer.sentinel == nullptr || + (actual_ptr_type->data.pointer.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, + actual_ptr_type->data.pointer.sentinel)); + if (!ok_null_term_ptrs) { + result.id = ConstCastResultIdPtrSentinel; + result.data.bad_ptr_sentinel = allocate_nonzero(1); + result.data.bad_ptr_sentinel->wanted_type = wanted_type; + return result; + } + bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len; + if (!(ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr)) { result.id = ConstCastResultIdPtrLens; - result.data.bad_ptr_lens = allocate_nonzero(1); - result.data.bad_ptr_lens->wanted_type = wanted_type; - result.data.bad_ptr_lens->actual_type = actual_type; return result; } @@ -9944,14 +9974,15 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted result.data.array_mismatch->actual_child = actual_type->data.array.child_type; return result; } - bool ok_null_terminated = !wanted_type->data.array.is_null_terminated || - actual_type->data.array.is_null_terminated; + bool ok_null_terminated = (wanted_type->data.array.sentinel == nullptr) || + (actual_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_type->data.array.sentinel, actual_type->data.array.sentinel)); if (!ok_null_terminated) { - result.id = ConstCastResultIdBadNullTermArrays; - result.data.bad_null_term_arrays = allocate_nonzero(1); - result.data.bad_null_term_arrays->child = child; - result.data.bad_null_term_arrays->wanted_type = wanted_type; - result.data.bad_null_term_arrays->actual_type = actual_type; + result.id = ConstCastResultIdSentinelArrays; + result.data.sentinel_arrays = allocate_nonzero(1); + result.data.sentinel_arrays->child = child; + result.data.sentinel_arrays->wanted_type = wanted_type; + result.data.sentinel_arrays->actual_type = actual_type; return result; } return result; @@ -10781,8 +10812,12 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT prev_type->data.pointer.child_type->id == ZigTypeIdArray && (cur_type->data.pointer.is_const || !prev_type->data.pointer.is_const || prev_type->data.pointer.child_type->data.array.len == 0) && - (cur_type->data.pointer.child_type->data.array.is_null_terminated || - !prev_type->data.pointer.child_type->data.array.is_null_terminated) && + ( + prev_type->data.pointer.child_type->data.array.sentinel == nullptr || + (cur_type->data.pointer.child_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, prev_type->data.pointer.child_type->data.array.sentinel, + cur_type->data.pointer.child_type->data.array.sentinel)) + ) && types_match_const_cast_only(ira, cur_type->data.pointer.child_type->data.array.child_type, prev_type->data.pointer.child_type->data.array.child_type, @@ -10798,8 +10833,12 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT cur_type->data.pointer.child_type->id == ZigTypeIdArray && (prev_type->data.pointer.is_const || !cur_type->data.pointer.is_const || cur_type->data.pointer.child_type->data.array.len == 0) && - (prev_type->data.pointer.child_type->data.array.is_null_terminated || - !cur_type->data.pointer.child_type->data.array.is_null_terminated) && + ( + cur_type->data.pointer.child_type->data.array.sentinel == nullptr || + (prev_type->data.pointer.child_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, cur_type->data.pointer.child_type->data.array.sentinel, + prev_type->data.pointer.child_type->data.array.sentinel)) + ) && types_match_const_cast_only(ira, prev_type->data.pointer.child_type->data.array.child_type, cur_type->data.pointer.child_type->data.array.child_type, @@ -10871,11 +10910,12 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } else if (prev_inst->value.type->id == ZigTypeIdPointer) { ZigType *array_type = prev_inst->value.type->data.pointer.child_type; src_assert(array_type->id == ZigTypeIdArray, source_node); - ZigType *ptr_type = get_pointer_to_type_extra( + ZigType *ptr_type = get_pointer_to_type_extra2( ira->codegen, array_type->data.array.child_type, prev_inst->value.type->data.pointer.is_const, false, - array_type->data.array.is_null_terminated ? PtrLenNull : PtrLenUnknown, - 0, 0, 0, false); + PtrLenUnknown, + 0, 0, 0, false, + VECTOR_INDEX_NONE, nullptr, array_type->data.array.sentinel); 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); @@ -11682,7 +11722,7 @@ static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *so IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; - if (types_have_same_zig_comptime_repr(wanted_type, payload_type)) { + if (types_have_same_zig_comptime_repr(ira->codegen, wanted_type, payload_type)) { copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); } else { const_instruction->base.value.data.x_optional = val; @@ -12601,14 +12641,24 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa break; } case ConstCastResultIdPtrLens: { - ZigType *wanted_type = cast_result->data.bad_ptr_lens->wanted_type; - ZigType *actual_type = cast_result->data.bad_ptr_lens->actual_type; - bool wanted_null_term = wanted_type->data.pointer.ptr_len == PtrLenNull; - bool actual_null_term = actual_type->data.pointer.ptr_len == PtrLenNull; - if (wanted_null_term && !actual_null_term) { - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("destination type requires null termination")); - } + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("pointer length mismatch")); + break; + } + case ConstCastResultIdPtrSentinel: { + ZigType *wanted_type = cast_result->data.bad_ptr_sentinel->wanted_type; + Buf *msg = buf_sprintf("destination pointer requires a terminating '"); + render_const_value(ira->codegen, msg, wanted_type->data.pointer.sentinel); + buf_appendf(msg, "' sentinel value"); + add_error_note(ira->codegen, parent_msg, source_node, msg); + break; + } + case ConstCastResultIdSentinelArrays: { + ZigType *wanted_type = cast_result->data.sentinel_arrays->wanted_type; + Buf *msg = buf_sprintf("destination array requires a terminating '"); + render_const_value(ira->codegen, msg, wanted_type->data.pointer.sentinel); + buf_appendf(msg, "' sentinel value"); + add_error_note(ira->codegen, parent_msg, source_node, msg); break; } case ConstCastResultIdCV: { @@ -12642,7 +12692,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa case ConstCastResultIdUnresolvedInferredErrSet: // TODO case ConstCastResultIdAsyncAllocatorType: // TODO case ConstCastResultIdArrayChild: // TODO - case ConstCastResultIdBadNullTermArrays: // TODO break; } } @@ -13013,16 +13062,18 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // *[N]T to [*]T and [*c]T if (wanted_type->id == ZigTypeIdPointer && - (wanted_type->data.pointer.ptr_len == PtrLenUnknown || wanted_type->data.pointer.ptr_len == PtrLenC || - wanted_type->data.pointer.ptr_len == PtrLenNull) && + (wanted_type->data.pointer.ptr_len == PtrLenUnknown || wanted_type->data.pointer.ptr_len == PtrLenC) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == ZigTypeIdArray && (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) { - if (wanted_type->data.pointer.ptr_len != PtrLenNull || - actual_type->data.pointer.child_type->data.array.is_null_terminated) + ZigType *actual_array_type = actual_type->data.pointer.child_type; + if (wanted_type->data.pointer.sentinel == nullptr || + (actual_array_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, + actual_array_type->data.array.sentinel))) { if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; @@ -14741,7 +14792,6 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { case PtrLenSingle: return lhs_type->data.pointer.child_type->id == ZigTypeIdArray; case PtrLenUnknown: - case PtrLenNull: case PtrLenC: return true; } @@ -15007,7 +15057,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i if (!op2_val) return ira->codegen->invalid_instruction; - bool is_null_terminated = false; + ConstExprValue *sentinel1 = nullptr; ConstExprValue *op1_array_val; size_t op1_array_index; size_t op1_array_end; @@ -15017,16 +15067,17 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op1_array_val = op1_val; op1_array_index = 0; op1_array_end = op1_type->data.array.len; + sentinel1 = op1_type->data.array.sentinel; } else if (op1_type->id == ZigTypeIdPointer && op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && - op1_type->data.pointer.ptr_len == PtrLenNull && + op1_type->data.pointer.sentinel != nullptr && op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray) { child_type = op1_type->data.pointer.child_type; op1_array_val = op1_val->data.x_ptr.data.base_array.array_val; op1_array_index = op1_val->data.x_ptr.data.base_array.elem_index; op1_array_end = op1_array_val->type->data.array.len; - is_null_terminated = true; + sentinel1 = op1_type->data.pointer.sentinel; } else if (is_slice(op1_type)) { ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index]->type_entry; child_type = ptr_type->data.pointer.child_type; @@ -15036,6 +15087,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; ConstExprValue *len_val = op1_val->data.x_struct.fields[slice_len_index]; op1_array_end = op1_array_index + bigint_as_usize(&len_val->data.x_bigint); + sentinel1 = ptr_type->data.pointer.sentinel; } else if (op1_type->id == ZigTypeIdPointer && op1_type->data.pointer.ptr_len == PtrLenSingle && op1_type->data.pointer.child_type->id == ZigTypeIdArray) { @@ -15046,13 +15098,14 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i return ira->codegen->invalid_instruction; op1_array_index = 0; op1_array_end = array_type->data.array.len; - is_null_terminated = is_null_terminated || array_type->data.array.is_null_terminated; + sentinel1 = array_type->data.array.sentinel; } else { ir_add_error(ira, op1, buf_sprintf("expected array, found '%s'", buf_ptr(&op1->value.type->name))); return ira->codegen->invalid_instruction; } + ConstExprValue *sentinel2 = nullptr; ConstExprValue *op2_array_val; size_t op2_array_index; size_t op2_array_end; @@ -15062,15 +15115,17 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_array_val = op2_val; op2_array_index = 0; op2_array_end = op2_array_val->type->data.array.len; + sentinel2 = op2_type->data.array.sentinel; } else if (op2_type->id == ZigTypeIdPointer && - op2_type->data.pointer.ptr_len == PtrLenNull && + op2_type->data.pointer.sentinel != nullptr && op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray) { op2_type_valid = op2_type->data.pointer.child_type == child_type; op2_array_val = op2_val->data.x_ptr.data.base_array.array_val; op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index; op2_array_end = op2_array_val->type->data.array.len; - is_null_terminated = true; + + sentinel2 = op2_type->data.pointer.sentinel; } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index]->type_entry; op2_type_valid = ptr_type->data.pointer.child_type == child_type; @@ -15080,6 +15135,8 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; ConstExprValue *len_val = op2_val->data.x_struct.fields[slice_len_index]; op2_array_end = op2_array_index + bigint_as_usize(&len_val->data.x_bigint); + + sentinel2 = ptr_type->data.pointer.sentinel; } else if (op2_type->id == ZigTypeIdPointer && op2_type->data.pointer.ptr_len == PtrLenSingle && op2_type->data.pointer.child_type->id == ZigTypeIdArray) { @@ -15090,7 +15147,8 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i return ira->codegen->invalid_instruction; op2_array_index = 0; op2_array_end = array_type->data.array.len; - is_null_terminated = is_null_terminated || array_type->data.array.is_null_terminated; + + sentinel2 = array_type->data.array.sentinel; } else { ir_add_error(ira, op2, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); @@ -15103,6 +15161,19 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i return ira->codegen->invalid_instruction; } + ConstExprValue *sentinel; + if (sentinel1 != nullptr && sentinel2 != nullptr) { + // When there is a sentinel mismatch, no sentinel on the result. The type system + // will catch this if it is a problem. + sentinel = const_values_equal(ira->codegen, sentinel1, sentinel2) ? sentinel1 : nullptr; + } else if (sentinel1 != nullptr) { + sentinel = sentinel1; + } else if (sentinel2 != nullptr) { + sentinel = sentinel2; + } else { + sentinel = nullptr; + } + // The type of result is populated in the following if blocks IrInstruction *result = ir_const(ira, &instruction->base, nullptr); ConstExprValue *out_val = &result->value; @@ -15110,24 +15181,25 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i ConstExprValue *out_array_val; size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index); if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) { - result->value.type = get_array_type(ira->codegen, child_type, new_len, false); + result->value.type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_array_val = out_val; } else if (op1_type->id == ZigTypeIdPointer || op2_type->id == ZigTypeIdPointer) { out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; - out_array_val->type = get_array_type(ira->codegen, child_type, new_len, is_null_terminated); + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = out_array_val; out_val->type = get_pointer_to_type(ira->codegen, out_array_val->type, true); } 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, false); + ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, child_type, + true, false, PtrLenUnknown, 0, 0, 0, false, + VECTOR_INDEX_NONE, nullptr, sentinel); result->value.type = get_slice_type(ira->codegen, ptr_type); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; - out_array_val->type = get_array_type(ira->codegen, child_type, new_len, false); + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_val->data.x_struct.fields = alloc_const_vals_ptrs(2); @@ -15141,12 +15213,12 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i out_val->data.x_struct.fields[slice_len_index]->special = ConstValSpecialStatic; bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index]->data.x_bigint, new_len); } else { - result->value.type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenNull, - 0, 0, 0, false); + result->value.type = get_pointer_to_type_extra2(ira->codegen, child_type, true, false, PtrLenUnknown, + 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, sentinel); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; - out_array_val->type = get_array_type(ira->codegen, child_type, new_len, false); + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = out_array_val; out_val->data.x_ptr.data.base_array.elem_index = 0; @@ -15159,26 +15231,36 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i return result; } - out_array_val->data.x_array.data.s_none.elements = create_const_vals(new_len); + uint64_t full_len = new_len + ((sentinel != nullptr) ? 1 : 0); + out_array_val->data.x_array.data.s_none.elements = create_const_vals(full_len); // TODO handle the buf case here for an optimization expand_undef_array(ira->codegen, op1_array_val); expand_undef_array(ira->codegen, op2_array_val); size_t next_index = 0; for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { - copy_const_val(&out_array_val->data.x_array.data.s_none.elements[next_index], - &op1_array_val->data.x_array.data.s_none.elements[i], true); + ConstExprValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(elem_dest_val, &op1_array_val->data.x_array.data.s_none.elements[i], false); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; } for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) { - copy_const_val(&out_array_val->data.x_array.data.s_none.elements[next_index], - &op2_array_val->data.x_array.data.s_none.elements[i], true); + ConstExprValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(elem_dest_val, &op2_array_val->data.x_array.data.s_none.elements[i], false); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; } - if (next_index < new_len) { - ConstExprValue *null_byte = &out_array_val->data.x_array.data.s_none.elements[next_index]; - init_const_unsigned_negative(null_byte, child_type, 0, false); + if (next_index < full_len) { + ConstExprValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(elem_dest_val, sentinel, false); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; next_index += 1; } - assert(next_index == new_len); + assert(next_index == full_len); return result; } @@ -15230,7 +15312,7 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * ZigType *child_type = array_type->data.array.child_type; ZigType *result_array_type = get_array_type(ira->codegen, child_type, new_array_len, - array_type->data.array.is_null_terminated); + array_type->data.array.sentinel); IrInstruction *array_result; if (array_val->special == ConstValSpecialUndef || array_val->data.x_array.special == ConstArraySpecialUndef) { @@ -15250,7 +15332,7 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * // TODO optimize the buf case expand_undef_array(ira->codegen, array_val); - size_t extra_null_term = array_type->data.array.is_null_terminated ? 1 : 0; + size_t extra_null_term = (array_type->data.array.sentinel != nullptr) ? 1 : 0; out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len + extra_null_term); uint64_t i = 0; @@ -15266,10 +15348,9 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * } assert(i == new_array_len); - if (array_type->data.array.is_null_terminated) { - ConstExprValue *null_value = get_null_value(array_type->data.array.child_type); + if (array_type->data.array.sentinel != nullptr) { ConstExprValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; - copy_const_val(elem_dest_val, null_value, false); + copy_const_val(elem_dest_val, array_type->data.array.sentinel, false); elem_dest_val->parent.id = ConstParentIdArray; elem_dest_val->parent.data.p_array.array_val = out_val; elem_dest_val->parent.data.p_array.elem_index = i; @@ -17565,7 +17646,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source size_t dst_size = type_size(codegen, out_val->type); if (dst_size <= src_size) { - if (src_size == dst_size && types_have_same_zig_comptime_repr(out_val->type, pointee->type)) { + if (src_size == dst_size && types_have_same_zig_comptime_repr(codegen, out_val->type, pointee->type)) { copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut != ConstPtrMutComptimeVar); return ErrorNone; } @@ -18316,11 +18397,11 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct uint64_t index = bigint_as_u64(&casted_elem_index->value.data.x_bigint); if (array_type->id == ZigTypeIdArray) { uint64_t array_len = array_type->data.array.len; - if (index == array_len && array_type->data.array.is_null_terminated) { + if (index == array_len && array_type->data.array.sentinel != nullptr) { ZigType *elem_type = array_type->data.array.child_type; - IrInstruction *null_element = ir_const(ira, &elem_ptr_instruction->base, elem_type); - null_element->value = *get_null_value(elem_type); - return ir_get_ref(ira, &elem_ptr_instruction->base, null_element, true, false); + IrInstruction *sentinel_elem = ir_const(ira, &elem_ptr_instruction->base, elem_type); + copy_const_val(&sentinel_elem->value, array_type->data.array.sentinel, false); + return ir_get_ref(ira, &elem_ptr_instruction->base, sentinel_elem, true, false); } if (index >= array_len) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, @@ -18337,7 +18418,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index, - nullptr); + nullptr, nullptr); } else if (return_type->data.pointer.explicit_alignment != 0) { // figure out the largest alignment possible @@ -18457,7 +18538,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct new_index = offset + index; ZigType *array_type = array_ptr_val->data.x_ptr.data.base_array.array_val->type; mem_size = array_type->data.array.len; - if (array_type->data.array.is_null_terminated) { + if (array_type->data.array.sentinel != nullptr) { mem_size += 1; } old_size = mem_size - offset; @@ -18579,7 +18660,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME, - nullptr); + nullptr, nullptr); } else { // runtime known element index switch (type_requires_comptime(ira->codegen, return_type)) { @@ -18783,7 +18864,7 @@ static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_n ZigType *elem_type = ira->codegen->builtin_types.entry_var; ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field); + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr); if (instr_is_comptime(container_ptr)) { IrInstruction *result = ir_const(ira, source_instr, field_ptr_type); @@ -19565,6 +19646,12 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, return ira->codegen->invalid_instruction; } + if (slice_type_instruction->sentinel != nullptr) { + lazy_slice_type->sentinel = slice_type_instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_slice_type->sentinel, LazyOk) == nullptr) + return ira->codegen->invalid_instruction; + } + lazy_slice_type->elem_type = slice_type_instruction->child_type->child; if (ir_resolve_type_lazy(ira, lazy_slice_type->elem_type) == nullptr) return ira->codegen->invalid_instruction; @@ -19572,7 +19659,6 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, lazy_slice_type->is_const = slice_type_instruction->is_const; lazy_slice_type->is_volatile = slice_type_instruction->is_volatile; lazy_slice_type->is_allowzero = slice_type_instruction->is_allow_zero; - lazy_slice_type->is_null_terminated = slice_type_instruction->is_null_terminated; return result; } @@ -19647,6 +19733,22 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, ZigType *child_type = ir_resolve_type(ira, child_type_value); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; + + ConstExprValue *sentinel_val; + if (array_type_instruction->sentinel != nullptr) { + IrInstruction *uncasted_sentinel = array_type_instruction->sentinel->child; + if (type_is_invalid(uncasted_sentinel->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *sentinel = ir_implicit_cast(ira, uncasted_sentinel, child_type); + if (type_is_invalid(sentinel->value.type)) + return ira->codegen->invalid_instruction; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ira->codegen->invalid_instruction; + } else { + sentinel_val = nullptr; + } + switch (child_type->id) { case ZigTypeIdInvalid: // handled above zig_unreachable(); @@ -19682,8 +19784,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; - ZigType *result_type = get_array_type(ira->codegen, child_type, size, - array_type_instruction->is_null_terminated); + ZigType *result_type = get_array_type(ira->codegen, child_type, size, sentinel_val); return ir_const_type(ira, &array_type_instruction->base, result_type); } } @@ -19802,7 +19903,7 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr 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, false); - bool same_comptime_repr = types_have_same_zig_comptime_repr(child_type, type_entry); + bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, child_type, type_entry); if (instr_is_comptime(base_ptr)) { ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); @@ -20759,7 +20860,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, if (container_type->id == ZigTypeIdArray) { ZigType *child_type = container_type->data.array.child_type; if (container_type->data.array.len != elem_count) { - ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count, false); + ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count, nullptr); ir_add_error(ira, &instruction->base, buf_sprintf("expected %s literal, found %s literal", @@ -21246,7 +21347,7 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr ConstExprValue *declaration_array = create_const_vals(1); declaration_array->special = ConstValSpecialStatic; - declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, false); + declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, nullptr); declaration_array->data.x_array.special = ConstArraySpecialNone; declaration_array->data.x_array.data.s_none.elements = create_const_vals(declaration_count); init_const_slice(ira->codegen, out_val, declaration_array, 0, declaration_count, false); @@ -21391,7 +21492,7 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr ConstExprValue *fn_arg_name_array = create_const_vals(1); fn_arg_name_array->special = ConstValSpecialStatic; fn_arg_name_array->type = get_array_type(ira->codegen, - get_slice_type(ira->codegen, u8_ptr), fn_arg_count, false); + get_slice_type(ira->codegen, u8_ptr), fn_arg_count, nullptr); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); @@ -21446,7 +21547,6 @@ static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) { case PtrLenSingle: return BuiltinPtrSizeOne; case PtrLenUnknown: - case PtrLenNull: return BuiltinPtrSizeMany; case PtrLenC: return BuiltinPtrSizeC; @@ -21527,11 +21627,21 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty fields[5]->special = ConstValSpecialStatic; fields[5]->type = ira->codegen->builtin_types.entry_bool; fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero; - // is_null_terminated: bool - ensure_field_index(result->type, "is_null_terminated", 6); + // sentinel: ?*const c_void + ZigType *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_c_void, true); + ensure_field_index(result->type, "sentinel", 6); fields[6]->special = ConstValSpecialStatic; - fields[6]->type = ira->codegen->builtin_types.entry_bool; - fields[6]->data.x_bool = attrs_type->data.pointer.ptr_len == PtrLenNull; + fields[6]->type = get_optional_type(ira->codegen, ptr_type); + if (attrs_type->data.pointer.sentinel == nullptr) { + fields[6]->data.x_optional = nullptr; + } else { + ConstExprValue *ptr_val = create_const_vals(1); + fields[6]->data.x_optional = ptr_val; + ptr_val->data.x_ptr.special = ConstPtrSpecialRef; + ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst; + ptr_val->data.x_ptr.data.ref.pointee = create_const_vals(1); + copy_const_val(ptr_val->data.x_ptr.data.ref.pointee, attrs_type->data.pointer.sentinel, false); + } return result; }; @@ -21652,11 +21762,22 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr fields[1]->special = ConstValSpecialStatic; fields[1]->type = ira->codegen->builtin_types.entry_type; fields[1]->data.x_type = type_entry->data.array.child_type; - // is_null_terminated: bool - ensure_field_index(result->type, "is_null_terminated", 2); + // sentinel: ?*const c_void fields[2]->special = ConstValSpecialStatic; - fields[2]->type = ira->codegen->builtin_types.entry_bool; - fields[2]->data.x_bool = type_entry->data.array.is_null_terminated; + ZigType *ptr_type = get_pointer_to_type(ira->codegen, + ira->codegen->builtin_types.entry_c_void, true); + fields[2]->type = get_optional_type(ira->codegen, ptr_type); + if (type_entry->data.array.sentinel == nullptr) { + fields[2]->data.x_optional = nullptr; + } else { + ConstExprValue *ptr_val = create_const_vals(1); + fields[2]->data.x_optional = ptr_val; + ptr_val->type = ptr_type; + ptr_val->data.x_ptr.special = ConstPtrSpecialRef; + ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst; + ptr_val->data.x_ptr.data.ref.pointee = create_const_vals(1); + copy_const_val(ptr_val->data.x_ptr.data.ref.pointee, type_entry->data.array.sentinel, false); + } break; } @@ -21744,7 +21865,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr ConstExprValue *enum_field_array = create_const_vals(1); enum_field_array->special = ConstValSpecialStatic; - enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count, false); + enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count, nullptr); enum_field_array->data.x_array.special = ConstArraySpecialNone; enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count); @@ -21792,7 +21913,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr uint32_t error_count = type_entry->data.error_set.err_count; ConstExprValue *error_array = create_const_vals(1); error_array->special = ConstValSpecialStatic; - error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, false); + error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, nullptr); error_array->data.x_array.special = ConstArraySpecialNone; error_array->data.x_array.data.s_none.elements = create_const_vals(error_count); @@ -21888,7 +22009,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr ConstExprValue *union_field_array = create_const_vals(1); union_field_array->special = ConstValSpecialStatic; - union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count, false); + union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count, nullptr); union_field_array->data.x_array.special = ConstArraySpecialNone; union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count); @@ -21968,7 +22089,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr ConstExprValue *struct_field_array = create_const_vals(1); struct_field_array->special = ConstValSpecialStatic; - struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count, false); + struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count, nullptr); struct_field_array->data.x_array.special = ConstArraySpecialNone; struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count); @@ -22071,7 +22192,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr ConstExprValue *fn_arg_array = create_const_vals(1); fn_arg_array->special = ConstValSpecialStatic; - fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count, false); + fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count, nullptr); fn_arg_array->data.x_array.special = ConstArraySpecialNone; fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); @@ -22169,6 +22290,17 @@ static ConstExprValue *get_const_field(IrAnalyze *ira, ConstExprValue *struct_va return struct_value->data.x_struct.fields[field_index]; } +static ConstExprValue *get_const_field_variant(IrAnalyze *ira, ConstExprValue *struct_value, + const char *name, size_t field_index) +{ + ConstExprValue *field_val = get_const_field(ira, struct_value, name, field_index); + assert(field_val->type->id == ZigTypeIdOptional); + ConstExprValue *opt_val = field_val->data.x_optional; + if (opt_val == nullptr) return nullptr; + assert(opt_val->type->id == ZigTypeIdPointer); + return const_ptr_pointee_unchecked(ira->codegen, opt_val); +} + static bool get_const_field_bool(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) { ConstExprValue *value = get_const_field(ira, struct_value, name, field_index); @@ -22232,7 +22364,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index); - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, + ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, get_const_field_meta_type(ira, payload, "child", 4), get_const_field_bool(ira, payload, "is_const", 1), get_const_field_bool(ira, payload, "is_volatile", 2), @@ -22240,7 +22372,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi bigint_as_u32(get_const_field_lit_int(ira, payload, "alignment", 3)), 0, // bit_offset_in_host 0, // host_int_bytes - get_const_field_bool(ira, payload, "is_allowzero", 5) + get_const_field_bool(ira, payload, "is_allowzero", 5), + VECTOR_INDEX_NONE, + nullptr, + get_const_field_variant(ira, payload, "sentinel", 6) ); if (size_enum_index != 2) return ptr_type; @@ -22252,7 +22387,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi return get_array_type(ira->codegen, get_const_field_meta_type(ira, payload, "child", 1), bigint_as_u64(get_const_field_lit_int(ira, payload, "len", 0)), - get_const_field_bool(ira, payload, "is_null_terminated", 2) + get_const_field_variant(ira, payload, "sentinel", 2) ); case ZigTypeIdComptimeFloat: return ira->codegen->builtin_types.entry_num_lit_float; @@ -22635,7 +22770,7 @@ static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstru } ZigType *result_type = get_array_type(ira->codegen, - ira->codegen->builtin_types.entry_u8, buf_len(file_contents), false); + ira->codegen->builtin_types.entry_u8, buf_len(file_contents), nullptr); IrInstruction *result = ir_const(ira, &instruction->base, result_type); init_const_str_lit(ira->codegen, &result->value, file_contents); return result; @@ -25858,6 +25993,12 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct result->value.data.x_lazy = &lazy_ptr_type->base; lazy_ptr_type->base.id = LazyValueIdPtrType; + if (instruction->sentinel != nullptr) { + lazy_ptr_type->sentinel = instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_ptr_type->sentinel, LazyOk) == nullptr) + return ira->codegen->invalid_instruction; + } + lazy_ptr_type->elem_type = instruction->child_type->child; if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr) return ira->codegen->invalid_instruction; @@ -27804,6 +27945,20 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { if (type_is_invalid(elem_type)) return ErrorSemanticAnalyzeFail; + ConstExprValue *sentinel_val; + if (lazy_slice_type->sentinel != nullptr) { + if (type_is_invalid(lazy_slice_type->sentinel->value.type)) + return ErrorSemanticAnalyzeFail; + IrInstruction *sentinel = ir_implicit_cast(ira, lazy_slice_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value.type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } else { + sentinel_val = nullptr; + } + uint32_t align_bytes = 0; if (lazy_slice_type->align_inst != nullptr) { if (!ir_resolve_align(ira, lazy_slice_type->align_inst, elem_type, &align_bytes)) @@ -27849,11 +28004,12 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown; if ((err = type_resolve(ira->codegen, elem_type, needed_status))) return err; - ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, + ZigType *slice_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, lazy_slice_type->is_const, lazy_slice_type->is_volatile, - lazy_slice_type->is_null_terminated ? PtrLenNull : PtrLenUnknown, + PtrLenUnknown, align_bytes, - 0, 0, lazy_slice_type->is_allowzero); + 0, 0, lazy_slice_type->is_allowzero, + VECTOR_INDEX_NONE, nullptr, sentinel_val); val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type); @@ -27867,6 +28023,20 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { if (type_is_invalid(elem_type)) return ErrorSemanticAnalyzeFail; + ConstExprValue *sentinel_val; + if (lazy_ptr_type->sentinel != nullptr) { + if (type_is_invalid(lazy_ptr_type->sentinel->value.type)) + return ErrorSemanticAnalyzeFail; + IrInstruction *sentinel = ir_implicit_cast(ira, lazy_ptr_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value.type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } else { + sentinel_val = nullptr; + } + uint32_t align_bytes = 0; if (lazy_ptr_type->align_inst != nullptr) { if (!ir_resolve_align(ira, lazy_ptr_type->align_inst, elem_type, &align_bytes)) @@ -27909,10 +28079,10 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { } bool allow_zero = lazy_ptr_type->is_allowzero || lazy_ptr_type->ptr_len == PtrLenC; assert(val->type->id == ZigTypeIdMetaType); - val->data.x_type = get_pointer_to_type_extra(ira->codegen, elem_type, + val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type, lazy_ptr_type->is_const, lazy_ptr_type->is_volatile, lazy_ptr_type->ptr_len, align_bytes, lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes, - allow_zero); + allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val); val->special = ConstValSpecialStatic; return ErrorNone; } diff --git a/src/parser.cpp b/src/parser.cpp index c3f719425b..61f3120d0e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1833,8 +1833,10 @@ static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) { return loop; } - if (label != nullptr) - ast_invalid_token_error(pc, peek_token(pc)); + if (label != nullptr) { + put_back_token(pc); + put_back_token(pc); + } return nullptr; } @@ -1931,15 +1933,11 @@ static AstNode *ast_parse_asm_output(ParseContext *pc) { // AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) { - Token *sym_name = eat_token_if(pc, TokenIdBracketUnderscoreBracket); - if (sym_name == nullptr) { - if (eat_token_if(pc, TokenIdLBracket) == nullptr) { - return nullptr; - } else { - sym_name = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdRBracket); - } - } + if (eat_token_if(pc, TokenIdLBracket) == nullptr) + return nullptr; + + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); Token *str = expect_token(pc, TokenIdStringLiteral); expect_token(pc, TokenIdLParen); @@ -1954,7 +1952,7 @@ static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) { expect_token(pc, TokenIdRParen); AsmOutput *res = allocate(1); - res->asm_symbolic_name = (sym_name->id == TokenIdBracketUnderscoreBracket) ? buf_create_from_str("_") : token_buf(sym_name); + res->asm_symbolic_name = token_buf(sym_name); res->constraint = token_buf(str); res->variable_name = token_buf(var_name); res->return_type = return_type; @@ -1977,15 +1975,11 @@ static AstNode *ast_parse_asm_input(ParseContext *pc) { // AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN static AsmInput *ast_parse_asm_input_item(ParseContext *pc) { - Token *sym_name = eat_token_if(pc, TokenIdBracketUnderscoreBracket); - if (sym_name == nullptr) { - if (eat_token_if(pc, TokenIdLBracket) == nullptr) { - return nullptr; - } else { - sym_name = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdRBracket); - } - } + if (eat_token_if(pc, TokenIdLBracket) == nullptr) + return nullptr; + + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); Token *constraint = expect_token(pc, TokenIdStringLiteral); expect_token(pc, TokenIdLParen); @@ -1993,7 +1987,7 @@ static AsmInput *ast_parse_asm_input_item(ParseContext *pc) { expect_token(pc, TokenIdRParen); AsmInput *res = allocate(1); - res->asm_symbolic_name = (sym_name->id == TokenIdBracketUnderscoreBracket) ? buf_create_from_str("_") : token_buf(sym_name); + res->asm_symbolic_name = token_buf(sym_name); res->constraint = token_buf(constraint); res->expr = expr; return res; @@ -2613,42 +2607,28 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { put_back_token(pc); } - AstNode *array = ast_parse_array_type_start(pc); - if (array != nullptr) { - assert(array->type == NodeTypeArrayType); - while (true) { - if (eat_token_if(pc, TokenIdKeywordNull) != nullptr) { - array->data.array_type.is_null_terminated = true; - continue; + Token *arr_init_lbracket = eat_token_if(pc, TokenIdLBracket); + if (arr_init_lbracket != nullptr) { + Token *underscore = eat_token_if(pc, TokenIdSymbol); + if (underscore == nullptr) { + put_back_token(pc); + } else if (!buf_eql_str(token_buf(underscore), "_")) { + put_back_token(pc); + put_back_token(pc); + } else { + AstNode *sentinel = nullptr; + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); } - - 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; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { - array->data.array_type.is_const = true; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { - array->data.array_type.is_volatile = true; - continue; - } - break; + expect_token(pc, TokenIdRBracket); + AstNode *node = ast_create_node(pc, NodeTypeInferredArrayType, arr_init_lbracket); + node->data.inferred_array_type.sentinel = sentinel; + return node; } - - return array; } + AstNode *ptr = ast_parse_ptr_type_start(pc); if (ptr != nullptr) { assert(ptr->type == NodeTypePointerType); @@ -2657,11 +2637,6 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { if (child == nullptr) child = ptr; while (true) { - if (eat_token_if(pc, TokenIdKeywordNull) != nullptr) { - child->data.pointer_type.is_null_terminated = true; - continue; - } - Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); if (allowzero_token != nullptr) { child->data.pointer_type.allow_zero_token = allowzero_token; @@ -2699,9 +2674,35 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { return ptr; } - Token *arr_init = eat_token_if(pc, TokenIdBracketUnderscoreBracket); - if (arr_init != nullptr) { - return ast_create_node(pc, NodeTypeInferredArrayType, arr_init); + AstNode *array = ast_parse_array_type_start(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; + continue; + } + + if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { + array->data.array_type.is_const = true; + continue; + } + + if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { + array->data.array_type.is_volatile = true; + continue; + } + break; + } + + return array; } @@ -2775,9 +2776,15 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { return nullptr; AstNode *size = ast_parse_expr(pc); + AstNode *sentinel = nullptr; + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } expect_token(pc, TokenIdRBracket); AstNode *res = ast_create_node(pc, NodeTypeArrayType, lbracket); res->data.array_type.size = size; + res->data.array_type.sentinel = sentinel; return res; } @@ -2787,35 +2794,63 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { // / PTRUNKNOWN // / PTRC static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { + AstNode *sentinel = nullptr; + Token *asterisk = eat_token_if(pc, TokenIdStar); if (asterisk != nullptr) { + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk); res->data.pointer_type.star_token = asterisk; + res->data.pointer_type.sentinel = sentinel; return res; } Token *asterisk2 = eat_token_if(pc, TokenIdStarStar); if (asterisk2 != nullptr) { + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2); AstNode *res2 = ast_create_node(pc, NodeTypePointerType, asterisk2); res->data.pointer_type.star_token = asterisk2; res2->data.pointer_type.star_token = asterisk2; + res2->data.pointer_type.sentinel = sentinel; res->data.pointer_type.op_expr = res2; return res; } - Token *multptr = eat_token_if(pc, TokenIdBracketStarBracket); - if (multptr != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypePointerType, multptr); - res->data.pointer_type.star_token = multptr; - return res; - } + Token *lbracket = eat_token_if(pc, TokenIdLBracket); + if (lbracket != nullptr) { + Token *star = eat_token_if(pc, TokenIdStar); + if (star == nullptr) { + put_back_token(pc); + } else { + Token *c_tok = eat_token_if(pc, TokenIdSymbol); + if (c_tok != nullptr) { + if (!buf_eql_str(token_buf(c_tok), "c")) { + put_back_token(pc); // c symbol + } else { + expect_token(pc, TokenIdRBracket); + AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); + res->data.pointer_type.star_token = c_tok; + return res; + } + } - Token *cptr = eat_token_if(pc, TokenIdBracketStarCBracket); - if (cptr != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypePointerType, cptr); - res->data.pointer_type.star_token = cptr; - return res; + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } + expect_token(pc, TokenIdRBracket); + AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); + res->data.pointer_type.star_token = lbracket; + res->data.pointer_type.sentinel = sentinel; + return res; + } } return nullptr; @@ -3093,10 +3128,12 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont break; case NodeTypeArrayType: visit_field(&node->data.array_type.size, visit, context); + visit_field(&node->data.array_type.sentinel, visit, context); visit_field(&node->data.array_type.child_type, visit, context); visit_field(&node->data.array_type.align_expr, visit, context); break; case NodeTypeInferredArrayType: + visit_field(&node->data.array_type.sentinel, visit, context); visit_field(&node->data.array_type.child_type, visit, context); break; case NodeTypeAnyFrameType: @@ -3106,6 +3143,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont // none break; case NodeTypePointerType: + visit_field(&node->data.pointer_type.sentinel, visit, context); visit_field(&node->data.pointer_type.align_expr, visit, context); visit_field(&node->data.pointer_type.op_expr, visit, context); break; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 5088fbdbf8..f8b5059bf1 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -222,10 +222,6 @@ enum TokenizeState { TokenizeStateSawAtSign, TokenizeStateCharCode, TokenizeStateError, - TokenizeStateLBracket, - TokenizeStateLBracketStar, - TokenizeStateLBracketStarC, - TokenizeStateLBracketUnderscore, }; @@ -480,8 +476,8 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); break; case '[': - t.state = TokenizeStateLBracket; begin_token(&t, TokenIdLBracket); + end_token(&t); break; case ']': begin_token(&t, TokenIdRBracket); @@ -775,62 +771,6 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; - case TokenizeStateLBracket: - switch (c) { - case '*': - t.state = TokenizeStateLBracketStar; - break; - case '_': - t.state = TokenizeStateLBracketUnderscore; - break; - default: - // reinterpret as just an lbracket - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateLBracketUnderscore: - switch (c) { - case ']': - set_token_id(&t, t.cur_tok, TokenIdBracketUnderscoreBracket); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - // reinterpret as just an lbracket - t.pos -= 2; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateLBracketStar: - switch (c) { - case 'c': - t.state = TokenizeStateLBracketStarC; - set_token_id(&t, t.cur_tok, TokenIdBracketStarCBracket); - break; - case ']': - set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - invalid_char_error(&t, c); - } - break; - case TokenizeStateLBracketStarC: - switch (c) { - case ']': - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - invalid_char_error(&t, c); - } - break; case TokenizeStateSawPlusPercent: switch (c) { case '=': @@ -1525,7 +1465,6 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateLineString: case TokenizeStateLineStringEnd: case TokenizeStateSawBarBar: - case TokenizeStateLBracket: case TokenizeStateDocComment: case TokenizeStateContainerDocComment: end_token(&t); @@ -1534,9 +1473,6 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawBackslash: case TokenizeStateLineStringContinue: case TokenizeStateLineStringContinueC: - case TokenizeStateLBracketStar: - case TokenizeStateLBracketStarC: - case TokenizeStateLBracketUnderscore: tokenize_error(&t, "unexpected EOF"); break; case TokenizeStateLineComment: @@ -1576,8 +1512,6 @@ const char * token_name(TokenId id) { case TokenIdBitShiftRight: return ">>"; case TokenIdBitShiftRightEq: return ">>="; case TokenIdBitXorEq: return "^="; - case TokenIdBracketStarBracket: return "[*]"; - case TokenIdBracketStarCBracket: return "[*c]"; case TokenIdCharLiteral: return "CharLiteral"; case TokenIdCmpEq: return "=="; case TokenIdCmpGreaterOrEq: return ">="; @@ -1681,7 +1615,6 @@ const char * token_name(TokenId id) { case TokenIdTimesPercent: return "*%"; case TokenIdTimesPercentEq: return "*%="; case TokenIdBarBarEq: return "||="; - case TokenIdBracketUnderscoreBracket: return "[_]"; case TokenIdCount: zig_unreachable(); } diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index b152eb079c..084512a576 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -28,9 +28,6 @@ enum TokenId { TokenIdBitShiftRight, TokenIdBitShiftRightEq, TokenIdBitXorEq, - TokenIdBracketStarBracket, - TokenIdBracketStarCBracket, - TokenIdBracketUnderscoreBracket, TokenIdCharLiteral, TokenIdCmpEq, TokenIdCmpGreaterOrEq, diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 5dd6064984..3fe5fced07 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -291,10 +291,9 @@ static TokenId ptr_len_to_token_id(PtrLen ptr_len) { case PtrLenSingle: return TokenIdStar; case PtrLenUnknown: - case PtrLenNull: - return TokenIdBracketStarBracket; + return TokenIdLBracket; case PtrLenC: - return TokenIdBracketStarCBracket; + return TokenIdSymbol; } zig_unreachable(); } @@ -303,7 +302,6 @@ static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_vo AstNode *node = trans_create_node(c, NodeTypePointerType); node->data.pointer_type.star_token = allocate(1); node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len); - node->data.pointer_type.is_null_terminated = (ptr_len == PtrLenNull); node->data.pointer_type.is_const = is_const; node->data.pointer_type.is_volatile = is_volatile; node->data.pointer_type.op_expr = child_node; diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig index b9000125b3..47e74cd310 100644 --- a/test/stage1/behavior/array.zig +++ b/test/stage1/behavior/array.zig @@ -351,7 +351,7 @@ test "anonymous literal in array" { test "access the null element of a null terminated array" { const S = struct { fn doTheTest() void { - var array: [4]null u8 = .{'a', 'o', 'e', 'u'}; + var array: [4:0]u8 = .{'a', 'o', 'e', 'u'}; comptime expect(array[4] == 0); var len: usize = 4; expect(array[len] == 0); diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index affd7afe5e..9fccff51e3 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -226,7 +226,7 @@ fn testCastConstArrayRefToConstSlice() void { { const blah = "aoeu".*; const const_array_ref = &blah; - expect(@typeOf(const_array_ref) == *const [4]null u8); + expect(@typeOf(const_array_ref) == *const [4:0]u8); const slice: []const u8 = const_array_ref; expect(mem.eql(u8, slice, "aoeu")); } diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index a0188dcedd..6ac745f5c5 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -362,8 +362,8 @@ test "string concatenation" { const a = "OK" ++ " IT " ++ "WORKED"; const b = "OK IT WORKED"; - comptime expect(@typeOf(a) == *const [12]null u8); - comptime expect(@typeOf(b) == *const [12]null u8); + comptime expect(@typeOf(a) == *const [12:0]u8); + comptime expect(@typeOf(b) == *const [12:0]u8); const len = mem.len(u8, b); const len_with_null = len + 1; diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig index 7d8bdea569..6529a59059 100644 --- a/test/stage1/behavior/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -205,7 +205,7 @@ test "null terminated pointer" { const S = struct { fn doTheTest() void { var array_with_zero = [_]u8{'h', 'e', 'l', 'l', 'o', 0}; - var zero_ptr: [*]null const u8 = @ptrCast([*]null const u8, &array_with_zero); + var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero); var no_zero_ptr: [*]const u8 = zero_ptr; expect(std.mem.eql(u8, std.mem.toSliceConst(u8, no_zero_ptr), "hello")); } diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig index b1bb2e85bd..e3ce4d0904 100644 --- a/test/stage1/behavior/type.zig +++ b/test/stage1/behavior/type.zig @@ -98,21 +98,21 @@ test "Type.Array" { .Array = TypeInfo.Array{ .len = 123, .child = u8, - .is_null_terminated = false, + .sentinel = null, }, })); testing.expect([2]u32 == @Type(TypeInfo{ .Array = TypeInfo.Array{ .len = 2, .child = u32, - .is_null_terminated = false, + .sentinel = null, }, })); - testing.expect([2]null u32 == @Type(TypeInfo{ + testing.expect([2:0]u32 == @Type(TypeInfo{ .Array = TypeInfo.Array{ .len = 2, .child = u32, - .is_null_terminated = true, + .sentinel = &0, }, })); testTypes([_]type{ [1]u8, [30]usize, [7]bool }); diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index 4da2bb3ca2..dcd4da3d0f 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -71,17 +71,17 @@ test "type info: null terminated pointer type info" { } fn testNullTerminatedPtr() void { - const ptr_info = @typeInfo([*]null u8); + const ptr_info = @typeInfo([*:0]u8); expect(@as(TypeId, ptr_info) == TypeId.Pointer); expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); expect(ptr_info.Pointer.is_const == false); expect(ptr_info.Pointer.is_volatile == false); expect(ptr_info.Pointer.is_null_terminated == true); - expect(@typeInfo([]null u8).Pointer.is_null_terminated == true); - expect(@typeInfo([10]null u8).Array.is_null_terminated == true); - expect(@typeInfo([10]null u8).Array.len == 10); - expect(@sizeOf([10]null u8) == 11); + expect(@typeInfo([:0]u8).Pointer.sentinel != null); + expect(@typeInfo([10:0]u8).Array.sentinel != null); + expect(@typeInfo([10:0]u8).Array.len == 10); + expect(@sizeOf([10:0]u8) == 11); } test "type info: C pointer type info" {