diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 9f1e52428c..56c3426d8b 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -161,6 +161,7 @@ pub const TypeInfo = union(enum) { pub const Array = struct { len: comptime_int, child: type, + is_null_terminated: bool, }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 0a7bfe2f56..9072626c86 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -137,6 +137,7 @@ pub const Error = union(enum) { ExpectedCallOrFnProto: ExpectedCallOrFnProto, ExpectedSliceOrRBracket: ExpectedSliceOrRBracket, ExtraAlignQualifier: ExtraAlignQualifier, + ExtraNullQualifier: ExtraNullQualifier, ExtraConstQualifier: ExtraConstQualifier, ExtraVolatileQualifier: ExtraVolatileQualifier, ExtraAllowZeroQualifier: ExtraAllowZeroQualifier, @@ -184,6 +185,7 @@ pub const Error = union(enum) { .ExpectedCallOrFnProto => |*x| return x.render(tokens, stream), .ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream), .ExtraAlignQualifier => |*x| return x.render(tokens, stream), + .ExtraNullQualifier => |*x| return x.render(tokens, stream), .ExtraConstQualifier => |*x| return x.render(tokens, stream), .ExtraVolatileQualifier => |*x| return x.render(tokens, stream), .ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream), @@ -233,6 +235,7 @@ pub const Error = union(enum) { .ExpectedCallOrFnProto => |x| return x.node.firstToken(), .ExpectedSliceOrRBracket => |x| return x.token, .ExtraAlignQualifier => |x| return x.token, + .ExtraNullQualifier => |x| return x.token, .ExtraConstQualifier => |x| return x.token, .ExtraVolatileQualifier => |x| return x.token, .ExtraAllowZeroQualifier => |x| return x.token, @@ -293,6 +296,7 @@ pub const Error = union(enum) { pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub"); pub const UnattachedDocComment = SimpleError("Unattached documentation comment"); pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); + pub const ExtraNullQualifier = SimpleError("Extra null qualifier"); pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier"); @@ -1538,7 +1542,7 @@ pub const Node = struct { pub const Op = union(enum) { AddressOf, - ArrayType: *Node, + ArrayType: ArrayInfo, Await, BitNot, BoolNot, @@ -1552,11 +1556,17 @@ pub const Node = struct { Try, }; + pub const ArrayInfo = struct { + len_expr: *Node, + null_token: ?TokenIndex, + }; + pub const PtrInfo = struct { allowzero_token: ?TokenIndex, align_info: ?Align, const_token: ?TokenIndex, volatile_token: ?TokenIndex, + null_token: ?TokenIndex, pub const Align = struct { node: *Node, @@ -1588,8 +1598,8 @@ pub const Node = struct { } }, - Op.ArrayType => |size_expr| { - if (i < 1) return size_expr; + Op.ArrayType => |array_info| { + if (i < 1) return array_info.len_expr; i -= 1; }, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 42dff71b2a..1190b89089 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1085,7 +1085,7 @@ fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.Suf const node = try arena.create(Node.SuffixOp); node.* = Node.SuffixOp{ .base = Node{ .id = .SuffixOp }, - .lhs = .{.node = undefined}, // set by caller + .lhs = .{ .node = undefined }, // set by caller .op = op, .rtoken = try expectToken(it, tree, .RBrace), }; @@ -1138,7 +1138,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { while (try parseSuffixOp(arena, it, tree)) |node| { switch (node.id) { - .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{.node = res}, + .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res }, .InfixOp => node.cast(Node.InfixOp).?.lhs = res, else => unreachable, } @@ -1154,7 +1154,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.SuffixOp); node.* = Node.SuffixOp{ .base = Node{ .id = .SuffixOp }, - .lhs = .{.node = res}, + .lhs = .{ .node = res }, .op = Node.SuffixOp.Op{ .Call = Node.SuffixOp.Op.Call{ .params = params.list, @@ -1171,7 +1171,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { while (true) { if (try parseSuffixOp(arena, it, tree)) |node| { switch (node.id) { - .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{.node = res}, + .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res }, .InfixOp => node.cast(Node.InfixOp).?.lhs = res, else => unreachable, } @@ -1182,7 +1182,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const call = try arena.create(Node.SuffixOp); call.* = Node.SuffixOp{ .base = Node{ .id = .SuffixOp }, - .lhs = .{.node = res}, + .lhs = .{ .node = res }, .op = Node.SuffixOp.Op{ .Call = Node.SuffixOp.Op.Call{ .params = params.list, @@ -1531,7 +1531,7 @@ fn parseAnonLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node // anon container literal if (try parseInitList(arena, it, tree)) |node| { - node.lhs = .{.dot = dot}; + node.lhs = .{ .dot = dot }; return &node.base; } @@ -2252,6 +2252,16 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node .SliceType => |*slice_type| { // Collect pointer qualifiers in any order, but disallow duplicates while (true) { + if (eatToken(it, .Keyword_null)) |null_token| { + if (slice_type.null_token != null) { + try tree.errors.push(AstError{ + .ExtraNullQualifier = AstError.ExtraNullQualifier{ .token = it.index }, + }); + return error.ParseError; + } + slice_type.null_token = null_token; + continue; + } if (try parseByteAlign(arena, it, tree)) |align_expr| { if (slice_type.align_info != null) { try tree.errors.push(AstError{ @@ -2313,6 +2323,10 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node &prefix_op.op.PtrType; while (true) { + if (eatToken(it, .Keyword_null)) |null_token| { + ptr_info.null_token = null_token; + continue; + } if (eatToken(it, .Keyword_align)) |align_token| { const lparen = try expectToken(it, tree, .LParen); const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ @@ -2460,9 +2474,15 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No const lbracket = eatToken(it, .LBracket) orelse return null; const expr = try parseExpr(arena, it, tree); const rbracket = try expectToken(it, tree, .RBracket); + const null_token = eatToken(it, .Keyword_null); - const op = if (expr) |element_type| - Node.PrefixOp.Op{ .ArrayType = element_type } + const op = if (expr) |len_expr| + Node.PrefixOp.Op{ + .ArrayType = .{ + .len_expr = len_expr, + .null_token = null_token, + }, + } else Node.PrefixOp.Op{ .SliceType = Node.PrefixOp.PtrInfo{ @@ -2470,6 +2490,7 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No .align_info = null, .const_token = null, .volatile_token = null, + .null_token = null, }, }; @@ -2505,6 +2526,7 @@ fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node .align_info = null, .const_token = null, .volatile_token = null, + .null_token = null, }, }, .rhs = undefined, // set by caller @@ -2522,6 +2544,7 @@ fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node .align_info = null, .const_token = null, .volatile_token = null, + .null_token = null, }, }, .rhs = undefined, // set by caller diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 2f420768bf..0f9d0a59cf 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1552,6 +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; \\ ); } @@ -1562,6 +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; \\ ); } @@ -1889,6 +1891,7 @@ test "zig fmt: arrays" { \\ 2, \\ }; \\ const a: [0]u8 = []u8{}; + \\ const x: [4]null u8 = undefined; \\} \\ ); diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 258a5493de..dab2d66319 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -423,6 +423,9 @@ fn renderExpression( else => @as(usize, 0), }; try renderTokenOffset(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None, star_offset); // * + if (ptr_info.null_token) |null_token| { + try renderToken(tree, stream, null_token, indent, start_col, Space.Space); // null + } if (ptr_info.allowzero_token) |allowzero_token| { try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero } @@ -499,9 +502,9 @@ fn renderExpression( } }, - ast.Node.PrefixOp.Op.ArrayType => |array_index| { + ast.Node.PrefixOp.Op.ArrayType => |array_info| { const lbracket = prefix_op_node.op_token; - const rbracket = tree.nextToken(array_index.lastToken()); + const rbracket = tree.nextToken(array_info.len_expr.lastToken()); try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ @@ -509,7 +512,7 @@ fn renderExpression( const ends_with_comment = tree.tokens.at(rbracket - 1).id == .LineComment; const new_indent = if (ends_with_comment) indent + indent_delta else indent; const new_space = if (ends_with_comment) Space.Newline else Space.None; - try renderExpression(allocator, stream, tree, new_indent, start_col, array_index, new_space); + try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space); if (starts_with_comment) { try stream.writeByte('\n'); } @@ -517,6 +520,9 @@ fn renderExpression( try stream.writeByteNTimes(' ', indent); } try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ] + if (array_info.null_token) |null_token| { + try renderToken(tree, stream, null_token, indent, start_col, Space.Space); // null + } }, ast.Node.PrefixOp.Op.BitNot, ast.Node.PrefixOp.Op.BoolNot, diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index e919977116..daa60fcb25 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1118,6 +1118,7 @@ fn transCreateNodePtrType( .align_info = null, .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, + .null_token = null, }, }, .rhs = undefined, // translate and set afterward diff --git a/src/all_types.hpp b/src/all_types.hpp index f5c9c67414..0fe43eaa3e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -358,6 +358,7 @@ struct LazyValueSliceType { bool is_const; bool is_volatile; bool is_allowzero; + bool is_null_terminated; }; struct LazyValuePtrType { @@ -1234,6 +1235,7 @@ struct ZigTypeFloat { struct ZigTypeArray { ZigType *child_type; uint64_t len; + bool is_null_terminated; }; struct TypeStructField { @@ -1775,6 +1777,7 @@ struct TypeId { struct { ZigType *child_type; uint64_t size; + bool is_null_terminated; } array; struct { bool is_signed; @@ -2986,6 +2989,7 @@ struct IrInstructionArrayType { IrInstruction *size; IrInstruction *child_type; + bool is_null_terminated; }; struct IrInstructionPtrType { @@ -3015,6 +3019,7 @@ struct IrInstructionSliceType { 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 d84ba1d37c..f67c4a035e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -752,11 +752,12 @@ 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) { +ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, bool is_null_terminated) { TypeId type_id = {}; type_id.id = ZigTypeIdArray; 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; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) { return existing_entry->value; @@ -769,12 +770,14 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size) { buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[%" ZIG_PRI_u64 "]%s", array_size, buf_ptr(&child_type->name)); - entry->size_in_bits = child_type->size_in_bits * array_size; + size_t full_array_size = array_size + (is_null_terminated ? 1 : 0); + entry->size_in_bits = child_type->size_in_bits * full_array_size; entry->abi_align = child_type->abi_align; - entry->abi_size = child_type->abi_size * array_size; + entry->abi_size = child_type->abi_size * full_array_size; entry->data.array.child_type = child_type; entry->data.array.len = array_size; + entry->data.array.is_null_terminated = is_null_terminated; g->type_table.put(type_id, entry); return entry; @@ -782,7 +785,7 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size) { ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { assert(ptr_type->id == ZigTypeIdPointer); - assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown); + assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown || ptr_type->data.pointer.ptr_len == PtrLenNull); ZigType **parent_pointer = &ptr_type->data.pointer.slice_parent; if (*parent_pointer) { @@ -5615,7 +5618,7 @@ void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { } const_val->special = ConstValSpecialStatic; - const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str)); + const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str), false); const_val->data.x_array.special = ConstArraySpecialBuf; const_val->data.x_array.data.s_buf = str; @@ -5633,7 +5636,7 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { size_t len_with_null = buf_len(str) + 1; ConstExprValue *array_val = create_const_vals(1); array_val->special = ConstValSpecialStatic; - array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null); + array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null, false); // TODO buf optimization array_val->data.x_array.data.s_none.elements = create_const_vals(len_with_null); for (size_t i = 0; i < buf_len(str); i += 1) { @@ -6071,7 +6074,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), 0}); + get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, false), 0}); } frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name), @@ -6279,7 +6282,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), 0}); + get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, false), 0}); } for (size_t alloca_i = 0; alloca_i < fn->alloca_gen_list.length; alloca_i += 1) { @@ -7043,8 +7046,9 @@ uint32_t type_id_hash(TypeId x) { (((uint32_t)x.data.pointer.vector_index) ^ (uint32_t)0x19199716) + (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881); case ZigTypeIdArray: - return hash_ptr(x.data.array.child_type) + - ((uint32_t)x.data.array.size ^ (uint32_t)2122979968); + 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); case ZigTypeIdInt: return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) + (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557); @@ -7106,7 +7110,8 @@ 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.size == b.data.array.size && + a.data.array.is_null_terminated == b.data.array.is_null_terminated; case ZigTypeIdInt: return a.data.integer.is_signed == b.data.integer.is_signed && a.data.integer.bit_count == b.data.integer.bit_count; @@ -8292,7 +8297,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); + ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, false); LLVMTypeRef union_element_types[] = { most_aligned_union_member->type_entry->llvm_type, get_llvm_type(g, padding_array), @@ -8326,7 +8331,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); + ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, false); LLVMTypeRef union_element_types[] = { get_llvm_type(g, most_aligned_union_member->type_entry), get_llvm_type(g, padding_array), diff --git a/src/analyze.hpp b/src/analyze.hpp index ddfb31c286..a1e38a43d7 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -33,7 +33,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); +ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, bool is_null_terminated); 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); diff --git a/src/codegen.cpp b/src/codegen.cpp index a288f397fd..5def04575e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7465,7 +7465,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); + ZigType *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, false); 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)); @@ -9067,7 +9067,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); + test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length, false); 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/ir.cpp b/src/ir.cpp index 77fc1307ca..c910d4800d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1772,11 +1772,12 @@ 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) + IrInstruction *child_type, bool is_null_terminated) { IrInstructionArrayType *instruction = ir_build_instruction(irb, scope, source_node); instruction->size = size; instruction->child_type = child_type; + instruction->is_null_terminated = is_null_terminated; ir_ref_instruction(size, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); @@ -1795,7 +1796,8 @@ 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) + IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, bool is_allow_zero, + bool is_null_terminated) { IrInstructionSliceType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_const = is_const; @@ -1803,6 +1805,7 @@ static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode instruction->child_type = child_type; instruction->align_value = align_value; instruction->is_allow_zero = is_allow_zero; + instruction->is_null_terminated = is_null_terminated; ir_ref_instruction(child_type, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); @@ -6216,7 +6219,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A 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); + container_type = ir_build_array_type(irb, scope, node, item_count_inst, elem_type, false); } else { container_type = ir_gen_node(irb, container_init_expr->type, scope); if (container_type == irb->codegen->invalid_instruction) @@ -6944,6 +6947,7 @@ 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 *align_expr = node->data.array_type.align_expr; Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); @@ -6973,7 +6977,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); + return ir_build_array_type(irb, scope, node, size_value, child_type, is_null_terminated); } else { IrInstruction *align_value; if (align_expr != nullptr) { @@ -6988,7 +6992,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); + return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, align_value, is_allow_zero, + is_null_terminated); } } @@ -10631,6 +10636,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT // *[N]T to []T // *[N]T to E![]T if (cur_type->id == ZigTypeIdPointer && + cur_type->data.pointer.ptr_len == PtrLenSingle && cur_type->data.pointer.child_type->id == ZigTypeIdArray && ((prev_type->id == ZigTypeIdErrorUnion && is_slice(prev_type->data.error_union.payload_type)) || is_slice(prev_type))) @@ -10639,7 +10645,8 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT ZigType *slice_type = (prev_type->id == ZigTypeIdErrorUnion) ? prev_type->data.error_union.payload_type : prev_type; ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; - if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && + if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || + !cur_type->data.pointer.is_const) && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) @@ -10653,6 +10660,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT // *[N]T to E![]T if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.child_type->id == ZigTypeIdArray && + prev_type->data.pointer.ptr_len == PtrLenSingle && ((cur_type->id == ZigTypeIdErrorUnion && is_slice(cur_type->data.error_union.payload_type)) || is_slice(cur_type))) { @@ -10660,7 +10668,8 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT ZigType *slice_type = (cur_type->id == ZigTypeIdErrorUnion) ? cur_type->data.error_union.payload_type : cur_type; ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; - if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && + if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || + !prev_type->data.pointer.is_const) && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) @@ -14893,7 +14902,7 @@ 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); + result->value.type = get_array_type(ira->codegen, child_type, new_len, false); out_array_val = out_val; } else if (is_slice(op1_type) || is_slice(op2_type)) { @@ -14902,7 +14911,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i 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); + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, false); out_val->data.x_struct.fields = alloc_const_vals_ptrs(2); @@ -14923,7 +14932,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i 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); + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, false); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.is_cstr = true; out_val->data.x_ptr.data.base_array.array_val = out_array_val; @@ -14994,7 +15003,7 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * ZigType *child_type = array_type->data.array.child_type; IrInstruction *result = ir_const(ira, &instruction->base, - get_array_type(ira->codegen, child_type, new_array_len)); + get_array_type(ira->codegen, child_type, new_array_len, false)); ConstExprValue *out_val = &result->value; if (array_val->data.x_array.special == ConstArraySpecialUndef) { out_val->data.x_array.special = ConstArraySpecialUndef; @@ -19311,6 +19320,7 @@ 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; } @@ -19420,7 +19430,8 @@ 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); + ZigType *result_type = get_array_type(ira->codegen, child_type, size, + array_type_instruction->is_null_terminated); return ir_const_type(ira, &array_type_instruction->base, result_type); } } @@ -20496,7 +20507,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); + ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count, false); ir_add_error(ira, &instruction->base, buf_sprintf("expected %s literal, found %s literal", @@ -20983,7 +20994,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); + declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, false); 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); @@ -21128,7 +21139,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); + get_slice_type(ira->codegen, u8_ptr), fn_arg_count, false); 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); @@ -21376,7 +21387,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Array", nullptr); - ConstExprValue **fields = alloc_const_vals_ptrs(2); + ConstExprValue **fields = alloc_const_vals_ptrs(3); result->data.x_struct.fields = fields; // len: usize @@ -21389,6 +21400,11 @@ 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); + 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; break; } @@ -21476,7 +21492,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); + enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count, false); enum_field_array->data.x_array.special = ConstArraySpecialNone; enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count); @@ -21524,7 +21540,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); + error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, false); error_array->data.x_array.special = ConstArraySpecialNone; error_array->data.x_array.data.s_none.elements = create_const_vals(error_count); @@ -21620,7 +21636,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); + union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count, false); union_field_array->data.x_array.special = ConstArraySpecialNone; union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count); @@ -21700,7 +21716,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); + struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count, false); struct_field_array->data.x_array.special = ConstArraySpecialNone; struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count); @@ -21803,7 +21819,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); + fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count, false); fn_arg_array->data.x_array.special = ConstArraySpecialNone; fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); @@ -21983,7 +21999,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi assert(payload->type == ir_type_info_get_type(ira, "Array", nullptr)); 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)) + bigint_as_u64(get_const_field_lit_int(ira, payload, "len", 0)), + get_const_field_bool(ira, payload, "is_null_terminated", 2) ); case ZigTypeIdComptimeFloat: return ira->codegen->builtin_types.entry_num_lit_float; @@ -22366,7 +22383,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)); + ira->codegen->builtin_types.entry_u8, buf_len(file_contents), false); IrInstruction *result = ir_const(ira, &instruction->base, result_type); init_const_str_lit(ira->codegen, &result->value, file_contents); return result; @@ -27581,7 +27598,9 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { 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, - lazy_slice_type->is_const, lazy_slice_type->is_volatile, PtrLenUnknown, align_bytes, + lazy_slice_type->is_const, lazy_slice_type->is_volatile, + lazy_slice_type->is_null_terminated ? PtrLenNull : PtrLenUnknown, + align_bytes, 0, 0, lazy_slice_type->is_allowzero); val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdMetaType); diff --git a/test/stage1/behavior/if.zig b/test/stage1/behavior/if.zig index 9e93ceb656..63c31fb03e 100644 --- a/test/stage1/behavior/if.zig +++ b/test/stage1/behavior/if.zig @@ -81,6 +81,10 @@ test "if prongs cast to expected type instead of peer type resolution" { var x: i32 = 0; x = if (f) 1 else 2; expect(x == 2); + + var b = true; + const y: i32 = if (b) 1 else 2; + expect(y == 1); } }; S.doTheTest(false); diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig index b84369a164..b1bb2e85bd 100644 --- a/test/stage1/behavior/type.zig +++ b/test/stage1/behavior/type.zig @@ -11,103 +11,122 @@ fn testTypes(comptime types: []const type) void { } test "Type.MetaType" { - testing.expect(type == @Type(TypeInfo { .Type = undefined })); - testTypes([_]type {type}); + testing.expect(type == @Type(TypeInfo{ .Type = undefined })); + testTypes([_]type{type}); } test "Type.Void" { - testing.expect(void == @Type(TypeInfo { .Void = undefined })); - testTypes([_]type {void}); + testing.expect(void == @Type(TypeInfo{ .Void = undefined })); + testTypes([_]type{void}); } test "Type.Bool" { - testing.expect(bool == @Type(TypeInfo { .Bool = undefined })); - testTypes([_]type {bool}); + testing.expect(bool == @Type(TypeInfo{ .Bool = undefined })); + testTypes([_]type{bool}); } test "Type.NoReturn" { - testing.expect(noreturn == @Type(TypeInfo { .NoReturn = undefined })); - testTypes([_]type {noreturn}); + testing.expect(noreturn == @Type(TypeInfo{ .NoReturn = undefined })); + testTypes([_]type{noreturn}); } test "Type.Int" { - testing.expect(u1 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 1 } })); - testing.expect(i1 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 1 } })); - testing.expect(u8 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 8 } })); - testing.expect(i8 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 8 } })); - testing.expect(u64 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 64 } })); - testing.expect(i64 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 64 } })); - testTypes([_]type {u8,u32,i64}); + testing.expect(u1 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = false, .bits = 1 } })); + testing.expect(i1 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = true, .bits = 1 } })); + testing.expect(u8 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = false, .bits = 8 } })); + testing.expect(i8 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = true, .bits = 8 } })); + testing.expect(u64 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = false, .bits = 64 } })); + testing.expect(i64 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = true, .bits = 64 } })); + testTypes([_]type{ u8, u32, i64 }); } test "Type.Float" { - testing.expect(f16 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 16 } })); - testing.expect(f32 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 32 } })); - testing.expect(f64 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 64 } })); - testing.expect(f128 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 128 } })); - testTypes([_]type {f16, f32, f64, f128}); + testing.expect(f16 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 16 } })); + testing.expect(f32 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 32 } })); + testing.expect(f64 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 64 } })); + testing.expect(f128 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 128 } })); + testTypes([_]type{ f16, f32, f64, f128 }); } test "Type.Pointer" { - testTypes([_]type { + testTypes([_]type{ // One Value Pointer Types - *u8, *const u8, - *volatile u8, *const volatile u8, - *align(4) u8, *const align(4) u8, - *volatile align(4) u8, *const volatile align(4) u8, - *align(8) u8, *const align(8) u8, - *volatile align(8) u8, *const volatile align(8) u8, - *allowzero u8, *const allowzero u8, - *volatile allowzero u8, *const volatile allowzero u8, - *align(4) allowzero u8, *const align(4) allowzero u8, - *volatile align(4) allowzero u8, *const volatile align(4) allowzero u8, + *u8, *const u8, + *volatile u8, *const volatile u8, + *align(4) u8, *align(4) const u8, + *align(4) volatile u8, *align(4) const volatile u8, + *align(8) u8, *align(8) const u8, + *align(8) volatile u8, *align(8) const volatile u8, + *allowzero u8, *allowzero const u8, + *allowzero volatile u8, *allowzero const volatile u8, + *allowzero align(4) u8, *allowzero align(4) const u8, + *allowzero align(4) volatile u8, *allowzero align(4) const volatile u8, // Many Values Pointer Types - [*]u8, [*]const u8, - [*]volatile u8, [*]const volatile u8, - [*]align(4) u8, [*]const align(4) u8, - [*]volatile align(4) u8, [*]const volatile align(4) u8, - [*]align(8) u8, [*]const align(8) u8, - [*]volatile align(8) u8, [*]const volatile align(8) u8, - [*]allowzero u8, [*]const allowzero u8, - [*]volatile allowzero u8, [*]const volatile allowzero u8, - [*]align(4) allowzero u8, [*]const align(4) allowzero u8, - [*]volatile align(4) allowzero u8, [*]const volatile align(4) allowzero u8, + [*]u8, [*]const u8, + [*]volatile u8, [*]const volatile u8, + [*]align(4) u8, [*]align(4) const u8, + [*]align(4) volatile u8, [*]align(4) const volatile u8, + [*]align(8) u8, [*]align(8) const u8, + [*]align(8) volatile u8, [*]align(8) const volatile u8, + [*]allowzero u8, [*]allowzero const u8, + [*]allowzero volatile u8, [*]allowzero const volatile u8, + [*]allowzero align(4) u8, [*]allowzero align(4) const u8, + [*]allowzero align(4) volatile u8, [*]allowzero align(4) const volatile u8, // Slice Types - []u8, []const u8, - []volatile u8, []const volatile u8, - []align(4) u8, []const align(4) u8, - []volatile align(4) u8, []const volatile align(4) u8, - []align(8) u8, []const align(8) u8, - []volatile align(8) u8, []const volatile align(8) u8, - []allowzero u8, []const allowzero u8, - []volatile allowzero u8, []const volatile allowzero u8, - []align(4) allowzero u8, []const align(4) allowzero u8, - []volatile align(4) allowzero u8, []const volatile align(4) allowzero u8, + []u8, []const u8, + []volatile u8, []const volatile u8, + []align(4) u8, []align(4) const u8, + []align(4) volatile u8, []align(4) const volatile u8, + []align(8) u8, []align(8) const u8, + []align(8) volatile u8, []align(8) const volatile u8, + []allowzero u8, []allowzero const u8, + []allowzero volatile u8, []allowzero const volatile u8, + []allowzero align(4) u8, []allowzero align(4) const u8, + []allowzero align(4) volatile u8, []allowzero align(4) const volatile u8, // C Pointer Types - [*c]u8, [*c]const u8, - [*c]volatile u8, [*c]const volatile u8, - [*c]align(4) u8, [*c]const align(4) u8, - [*c]volatile align(4) u8, [*c]const volatile align(4) u8, - [*c]align(8) u8, [*c]const align(8) u8, - [*c]volatile align(8) u8, [*c]const volatile align(8) u8, + [*c]u8, [*c]const u8, + [*c]volatile u8, [*c]const volatile u8, + [*c]align(4) u8, [*c]align(4) const u8, + [*c]align(4) volatile u8, [*c]align(4) const volatile u8, + [*c]align(8) u8, [*c]align(8) const u8, + [*c]align(8) volatile u8, [*c]align(8) const volatile u8, }); } test "Type.Array" { - testing.expect([123]u8 == @Type(TypeInfo { .Array = TypeInfo.Array { .len = 123, .child = u8 } })); - testing.expect([2]u32 == @Type(TypeInfo { .Array = TypeInfo.Array { .len = 2, .child = u32 } })); - testTypes([_]type {[1]u8, [30]usize, [7]bool}); + testing.expect([123]u8 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 123, + .child = u8, + .is_null_terminated = false, + }, + })); + testing.expect([2]u32 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 2, + .child = u32, + .is_null_terminated = false, + }, + })); + testing.expect([2]null u32 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 2, + .child = u32, + .is_null_terminated = true, + }, + })); + testTypes([_]type{ [1]u8, [30]usize, [7]bool }); } test "Type.ComptimeFloat" { - testTypes([_]type {comptime_float}); + testTypes([_]type{comptime_float}); } test "Type.ComptimeInt" { - testTypes([_]type {comptime_int}); + testTypes([_]type{comptime_int}); } test "Type.Undefined" { - testTypes([_]type {@typeOf(undefined)}); + testTypes([_]type{@typeOf(undefined)}); } test "Type.Null" { - testTypes([_]type {@typeOf(null)}); + testTypes([_]type{@typeOf(null)}); } diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index ed64e3e430..4da2bb3ca2 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -46,6 +46,7 @@ fn testPointer() void { expect(u32_ptr_info.Pointer.is_volatile == false); expect(u32_ptr_info.Pointer.alignment == @alignOf(u32)); expect(u32_ptr_info.Pointer.child == u32); + expect(u32_ptr_info.Pointer.is_null_terminated == false); } test "type info: unknown length pointer type info" { @@ -55,14 +56,34 @@ test "type info: unknown length pointer type info" { fn testUnknownLenPtr() void { const u32_ptr_info = @typeInfo([*]const volatile f64); - expect(@as(TypeId,u32_ptr_info) == TypeId.Pointer); + expect(@as(TypeId, u32_ptr_info) == TypeId.Pointer); expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); expect(u32_ptr_info.Pointer.is_const == true); expect(u32_ptr_info.Pointer.is_volatile == true); + expect(u32_ptr_info.Pointer.is_null_terminated == false); expect(u32_ptr_info.Pointer.alignment == @alignOf(f64)); expect(u32_ptr_info.Pointer.child == f64); } +test "type info: null terminated pointer type info" { + testNullTerminatedPtr(); + comptime testNullTerminatedPtr(); +} + +fn testNullTerminatedPtr() void { + const ptr_info = @typeInfo([*]null 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); +} + test "type info: C pointer type info" { testCPtr(); comptime testCPtr(); @@ -70,7 +91,7 @@ test "type info: C pointer type info" { fn testCPtr() void { const ptr_info = @typeInfo([*c]align(4) const i8); - expect(@as(TypeId,ptr_info) == TypeId.Pointer); + expect(@as(TypeId, ptr_info) == TypeId.Pointer); expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.C); expect(ptr_info.Pointer.is_const); expect(!ptr_info.Pointer.is_volatile); @@ -288,13 +309,13 @@ test "type info: anyframe and anyframe->T" { fn testAnyFrame() void { { const anyframe_info = @typeInfo(anyframe->i32); - expect(@as(TypeId,anyframe_info) == .AnyFrame); + expect(@as(TypeId, anyframe_info) == .AnyFrame); expect(anyframe_info.AnyFrame.child.? == i32); } { const anyframe_info = @typeInfo(anyframe); - expect(@as(TypeId,anyframe_info) == .AnyFrame); + expect(@as(TypeId, anyframe_info) == .AnyFrame); expect(anyframe_info.AnyFrame.child == null); } } @@ -334,7 +355,7 @@ test "type info: extern fns with and without lib names" { if (std.mem.eql(u8, decl.name, "bar1")) { expect(decl.data.Fn.lib_name == null); } else { - std.testing.expectEqual(@as([]const u8,"cool"), decl.data.Fn.lib_name.?); + std.testing.expectEqual(@as([]const u8, "cool"), decl.data.Fn.lib_name.?); } } }