From fd6b79e67f0cf91a72c121fc6fd484a03f487506 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 6 Jan 2023 18:21:21 +0200 Subject: [PATCH 1/7] Sema: restore status on layout resolution failure `resolveTypeFields` and `resolveTypeFully` already do this. Closes #14059 --- src/Sema.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 95de857616..6fa2f92dd7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -29847,6 +29847,11 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } + const prev_status = struct_obj.status; + errdefer if (struct_obj.status == .layout_wip) { + struct_obj.status = prev_status; + }; + struct_obj.status = .layout_wip; for (struct_obj.fields.values()) |field, i| { sema.resolveTypeLayout(field.ty) catch |err| switch (err) { @@ -30026,6 +30031,11 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } + const prev_status = union_obj.status; + errdefer if (union_obj.status == .layout_wip) { + union_obj.status = prev_status; + }; + union_obj.status = .layout_wip; for (union_obj.fields.values()) |field, i| { sema.resolveTypeLayout(field.ty) catch |err| switch (err) { From 83673a8b5f508376c81deaa4b01815474873eb69 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 6 Jan 2023 19:03:40 +0200 Subject: [PATCH 2/7] fix errdefers in functions that can't return errors This was broken by 58caed1c71179f48c4e7bffadef0392fa8381e72 Closes #14053 --- src/AstGen.zig | 3 ++- src/Sema.zig | 14 +++++++++++++- src/Zir.zig | 6 ++++++ src/print_zir.zig | 1 + test/behavior/defer.zig | 11 +++++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 7331ab6410..1d2244a5b5 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2463,6 +2463,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .is_non_null_ptr, .is_non_err, .is_non_err_ptr, + .ret_is_non_err, .mod_rem, .mul, .mulwrap, @@ -7012,7 +7013,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // Emit conditional branch for generating errdefers. const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; - const is_non_err = try gz.addUnNode(.is_non_err, result, node); + const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node); const condbr = try gz.addCondBr(.condbr, node); var then_scope = gz.makeSubBlock(scope); diff --git a/src/Sema.zig b/src/Sema.zig index 6fa2f92dd7..ec698f7a3b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -953,6 +953,7 @@ fn analyzeBodyInner( .int_type => try sema.zirIntType(block, inst), .is_non_err => try sema.zirIsNonErr(block, inst), .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), + .ret_is_non_err => try sema.zirRetIsNonErr(block, inst), .is_non_null => try sema.zirIsNonNull(block, inst), .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), .merge_error_sets => try sema.zirMergeErrorSets(block, inst), @@ -10288,6 +10289,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .ret_err_value_code, .restore_err_ret_index, .is_non_err, + .ret_is_non_err, .condbr, => {}, else => break, @@ -16577,7 +16579,7 @@ fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); try sema.checkErrorType(block, src, sema.typeOf(operand)); - return sema.analyzeIsNonErr(block, inst_data.src(), operand); + return sema.analyzeIsNonErr(block, src, operand); } fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -16592,6 +16594,16 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.analyzeIsNonErr(block, src, loaded); } +fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); + const operand = try sema.resolveInst(inst_data.operand); + return sema.analyzeIsNonErr(block, src, operand); +} + fn zirCondbr( sema: *Sema, parent_block: *Block, diff --git a/src/Zir.zig b/src/Zir.zig index 3f2e935050..94e6a9a11a 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -481,6 +481,9 @@ pub const Inst = struct { /// Return a boolean false if dereferenced pointer is an error /// Uses the `un_node` field. is_non_err_ptr, + /// Same as `is_non_er` but doesn't validate that the type can be an error. + /// Uses the `un_node` field. + ret_is_non_err, /// A labeled block of code that loops forever. At the end of the body will have either /// a `repeat` instruction or a `repeat_inline` instruction. /// Uses the `pl_node` field. The AST node is either a for loop or while loop. @@ -1083,6 +1086,7 @@ pub const Inst = struct { .is_non_null_ptr, .is_non_err, .is_non_err_ptr, + .ret_is_non_err, .mod_rem, .mul, .mulwrap, @@ -1386,6 +1390,7 @@ pub const Inst = struct { .is_non_null_ptr, .is_non_err, .is_non_err_ptr, + .ret_is_non_err, .mod_rem, .mul, .mulwrap, @@ -1640,6 +1645,7 @@ pub const Inst = struct { .is_non_null_ptr = .un_node, .is_non_err = .un_node, .is_non_err_ptr = .un_node, + .ret_is_non_err = .un_node, .loop = .pl_node, .repeat = .node, .repeat_inline = .node, diff --git a/src/print_zir.zig b/src/print_zir.zig index 49c97a5bc7..6e8923bed9 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -179,6 +179,7 @@ const Writer = struct { .is_non_null_ptr, .is_non_err, .is_non_err_ptr, + .ret_is_non_err, .typeof, .struct_init_empty, .type_info, diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index 9b83290a0a..32f63b1a46 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -148,3 +148,14 @@ test "simple else prong doesn't emit an error for unreachable else prong" { }; try expect(a == 1); } + +test "errdefer used in function that doesn't return an error" { + const S = struct { + fn foo() u8 { + var a: u8 = 5; + errdefer a += 1; + return a; + } + }; + try expect(S.foo() == 5); +} From ad259736e2200e93400e4f09916551ae65ca56ad Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 9 Jan 2023 17:44:41 +0200 Subject: [PATCH 3/7] Value: implement more pointer eql cases Closes #14234 --- src/value.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/value.zig b/src/value.zig index 45b2bfd341..5d38bcb7a9 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2193,8 +2193,16 @@ pub const Value = extern union { const payload_ty = ty.errorUnionPayload(); return eqlAdvanced(a_payload, payload_ty, b_payload, payload_ty, mod, opt_sema); }, - .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), - .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), + .eu_payload_ptr => { + const a_payload = a.castTag(.eu_payload_ptr).?.data; + const b_payload = b.castTag(.eu_payload_ptr).?.data; + return eqlAdvanced(a_payload.container_ptr, ty, b_payload.container_ptr, ty, mod, opt_sema); + }, + .opt_payload_ptr => { + const a_payload = a.castTag(.opt_payload_ptr).?.data; + const b_payload = b.castTag(.opt_payload_ptr).?.data; + return eqlAdvanced(a_payload.container_ptr, ty, b_payload.container_ptr, ty, mod, opt_sema); + }, .function => { const a_payload = a.castTag(.function).?.data; const b_payload = b.castTag(.function).?.data; From 8b1780d9396aa7bd919f2ec5e003f981bbce07d5 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 10 Jan 2023 14:04:38 +0200 Subject: [PATCH 4/7] Sema: fix condition for omitting comptime arg from function type Closes #14164 --- src/Sema.zig | 2 +- ...d_generic_function_param_type_mismatch.zig | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/nested_generic_function_param_type_mismatch.zig diff --git a/src/Sema.zig b/src/Sema.zig index ec698f7a3b..8263468fbf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9005,7 +9005,7 @@ fn zirParam( else => |e| return e, } or comptime_syntax; if (sema.inst_map.get(inst)) |arg| { - if (is_comptime) { + if (is_comptime and sema.preallocated_new_func != null) { // We have a comptime value for this parameter so it should be elided from the // function type of the function instruction in this block. const coerced_arg = try sema.coerce(block, param_ty, arg, src); diff --git a/test/cases/compile_errors/nested_generic_function_param_type_mismatch.zig b/test/cases/compile_errors/nested_generic_function_param_type_mismatch.zig new file mode 100644 index 0000000000..2c05a1dd4a --- /dev/null +++ b/test/cases/compile_errors/nested_generic_function_param_type_mismatch.zig @@ -0,0 +1,24 @@ +pub fn sort( + comptime T: type, + items: []T, + context: anytype, + lessThan: *const fn (context: @TypeOf(context), lhs: T, rhs: T) u32, +) void { + _ = items; + _ = lessThan; +} +fn foo(_: void, _: u8, _: u8) u32 { + return 0; +} +pub export fn entry() void { + var items = [_]u8{ 3, 5, 7, 2, 6, 9, 4 }; + sort(u8, &items, void, foo); +} + +// error +// backend=llvm +// target=native +// +// :15:28: error: expected type '*const fn(comptime type, u8, u8) u32', found '*const fn(void, u8, u8) u32' +// :15:28: note: pointer type child 'fn(void, u8, u8) u32' cannot cast into pointer type child 'fn(comptime type, u8, u8) u32' +// :15:28: note: non-generic function cannot cast into a generic function From e2adf3b61a32eae2182bcb94981a0b3706040f9a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 10 Jan 2023 15:04:14 +0200 Subject: [PATCH 5/7] parser: add helpful note for missing const/var before container level decl Closes #13888 --- lib/std/zig/Ast.zig | 4 ++++ lib/std/zig/parse.zig | 7 +++++++ lib/std/zig/parser_test.zig | 12 ++++++++++++ 3 files changed, 23 insertions(+) diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 0573622d6f..f312093aa3 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -362,6 +362,9 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { .wrong_equal_var_decl => { return stream.writeAll("variable initialized with '==' instead of '='"); }, + .var_const_decl => { + return stream.writeAll("use 'var' or 'const' to declare variable"); + }, .expected_token => { const found_tag = token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)]; @@ -2743,6 +2746,7 @@ pub const Error = struct { c_style_container, expected_var_const, wrong_equal_var_decl, + var_const_decl, zig_style_container, previous_field, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 3bb27975db..fdb122b19d 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -471,6 +471,13 @@ const Parser = struct { // There is not allowed to be a decl after a field with no comma. // Report error but recover parser. try p.warn(.expected_comma_after_field); + if (p.token_tags[p.tok_i] == .semicolon and p.token_tags[identifier] == .identifier) { + try p.warnMsg(.{ + .tag = .var_const_decl, + .is_note = true, + .token = identifier, + }); + } p.findNextContainerMember(); continue; }, diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 1b8a240642..42eb1abdde 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -5238,6 +5238,18 @@ test "zig fmt: missing const/var before local variable" { }); } +test "zig fmt: missing const/var before local variable" { + try testError( + \\std = foo, + \\std = foo; + \\*u32 = foo; + , &.{ + .expected_comma_after_field, + .var_const_decl, + .expected_comma_after_field, + }); +} + test "zig fmt: while continue expr" { try testCanonical( \\test { From 1658e4893d749c7d1ed378cd34b6cbe1a132c0e9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 10 Jan 2023 15:07:48 +0200 Subject: [PATCH 6/7] AstGen: add note pointing to tuple field Closes #14188 --- src/AstGen.zig | 17 ++++++++++++++++- .../cases/compile_errors/tuple_declarations.zig | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 1d2244a5b5..894acdd8f2 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4487,9 +4487,24 @@ fn structDeclInner( .container_field_align, .container_field, .@"comptime", + .test_decl, => continue, else => { - return astgen.failNode(member_node, "tuple declarations cannot contain declarations", .{}); + const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) { + .container_field_init, + .container_field_align, + .container_field, + => break maybe_tuple, + else => {}, + } else unreachable; + return astgen.failNodeNotes( + member_node, + "tuple declarations cannot contain declarations", + .{}, + &[_]u32{ + try astgen.errNoteNode(tuple_member, "tuple field here", .{}), + }, + ); }, } }; diff --git a/test/cases/compile_errors/tuple_declarations.zig b/test/cases/compile_errors/tuple_declarations.zig index ec72561816..9a181e9769 100644 --- a/test/cases/compile_errors/tuple_declarations.zig +++ b/test/cases/compile_errors/tuple_declarations.zig @@ -23,3 +23,4 @@ const T = struct { // :5:5: error: union field missing name // :8:5: error: tuple field has a name // :15:5: error: tuple declarations cannot contain declarations +// :12:5: note: tuple field here From d8128c272a087bbfc0b33a786c18267f49cd4982 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 10 Jan 2023 21:54:58 +0200 Subject: [PATCH 7/7] Sema: fix typeInfo decls with usingnamespace Closes #12403 --- src/Module.zig | 31 ++++++++++----- src/Sema.zig | 77 +++++++++++++++++++++++++------------ test/behavior/type_info.zig | 24 ++++++++++++ 3 files changed, 98 insertions(+), 34 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 4fcd2e7ba6..558ed498ca 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -528,10 +528,10 @@ pub const Decl = struct { /// Decl is marked alive, then it sends the Decl to the linker. Otherwise it /// deletes the Decl on the spot. alive: bool, - /// Whether the Decl is a `usingnamespace` declaration. - is_usingnamespace: bool, /// If true `name` is already fully qualified. name_fully_qualified: bool = false, + /// What kind of a declaration is this. + kind: Kind, /// Represents the position of the code in the output file. /// This is populated regardless of semantic analysis and code generation. @@ -551,6 +551,14 @@ pub const Decl = struct { /// typed_value may need to be regenerated. dependencies: DepsTable = .{}, + pub const Kind = enum { + @"usingnamespace", + @"test", + @"comptime", + named, + anon, + }; + pub const Index = enum(u32) { _, @@ -4438,7 +4446,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { // not the struct itself. try sema.resolveTypeLayout(decl_tv.ty); - if (decl.is_usingnamespace) { + if (decl.kind == .@"usingnamespace") { if (!decl_tv.ty.eql(Type.type, mod)) { return sema.fail(&block_scope, ty_src, "expected type, found {}", .{ decl_tv.ty.fmt(mod), @@ -4964,26 +4972,31 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err // Every Decl needs a name. var is_named_test = false; + var kind: Decl.Kind = .named; const decl_name: [:0]const u8 = switch (decl_name_index) { 0 => name: { if (export_bit) { const i = iter.usingnamespace_index; iter.usingnamespace_index += 1; + kind = .@"usingnamespace"; break :name try std.fmt.allocPrintZ(gpa, "usingnamespace_{d}", .{i}); } else { const i = iter.comptime_index; iter.comptime_index += 1; + kind = .@"comptime"; break :name try std.fmt.allocPrintZ(gpa, "comptime_{d}", .{i}); } }, 1 => name: { const i = iter.unnamed_test_index; iter.unnamed_test_index += 1; + kind = .@"test"; break :name try std.fmt.allocPrintZ(gpa, "test_{d}", .{i}); }, 2 => name: { is_named_test = true; const test_name = zir.nullTerminatedString(decl_doccomment_index); + kind = .@"test"; break :name try std.fmt.allocPrintZ(gpa, "decltest.{s}", .{test_name}); }, else => name: { @@ -4991,6 +5004,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err if (raw_name.len == 0) { is_named_test = true; const test_name = zir.nullTerminatedString(decl_name_index + 1); + kind = .@"test"; break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name}); } else { break :name try gpa.dupeZ(u8, raw_name); @@ -4998,8 +5012,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err }, }; const is_exported = export_bit and decl_name_index != 0; - const is_usingnamespace = export_bit and decl_name_index == 0; - if (is_usingnamespace) try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); + if (kind == .@"usingnamespace") try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); // We create a Decl for it regardless of analysis status. const gop = try namespace.decls.getOrPutContextAdapted( @@ -5012,8 +5025,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err if (!gop.found_existing) { const new_decl_index = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope); const new_decl = mod.declPtr(new_decl_index); + new_decl.kind = kind; new_decl.name = decl_name; - if (is_usingnamespace) { + if (kind == .@"usingnamespace") { namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, is_pub); } log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace }); @@ -5058,7 +5072,6 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err } new_decl.is_pub = is_pub; new_decl.is_exported = is_exported; - new_decl.is_usingnamespace = is_usingnamespace; new_decl.has_align = has_align; new_decl.has_linksection_or_addrspace = has_linksection_or_addrspace; new_decl.zir_decl_index = @intCast(u32, decl_sub_index); @@ -5076,7 +5089,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err decl.is_pub = is_pub; decl.is_exported = is_exported; - decl.is_usingnamespace = is_usingnamespace; + decl.kind = kind; decl.has_align = has_align; decl.has_linksection_or_addrspace = has_linksection_or_addrspace; decl.zir_decl_index = @intCast(u32, decl_sub_index); @@ -5635,7 +5648,7 @@ pub fn allocateNewDecl( .has_linksection_or_addrspace = false, .has_align = false, .alive = false, - .is_usingnamespace = false, + .kind = .anon, }; return decl_and_index.decl_index; diff --git a/src/Sema.zig b/src/Sema.zig index 8263468fbf..3db5eb5ba1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16217,11 +16217,54 @@ fn typeInfoDecls( }; try sema.queueFullTypeResolution(try declaration_ty.copy(sema.arena)); - const decls_len = if (opt_namespace) |ns| ns.decls.count() else 0; - const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len); - for (decls_vals) |*decls_val, i| { - const decl_index = opt_namespace.?.decls.keys()[i]; + var decl_vals = std.ArrayList(Value).init(sema.gpa); + defer decl_vals.deinit(); + + var seen_namespaces = std.AutoHashMap(*Namespace, void).init(sema.gpa); + defer seen_namespaces.deinit(); + + if (opt_namespace) |some| { + try sema.typeInfoNamespaceDecls(block, decls_anon_decl.arena(), some, &decl_vals, &seen_namespaces); + } + + const new_decl = try decls_anon_decl.finish( + try Type.Tag.array.create(decls_anon_decl.arena(), .{ + .len = decl_vals.items.len, + .elem_type = declaration_ty, + }), + try Value.Tag.aggregate.create( + decls_anon_decl.arena(), + try decls_anon_decl.arena().dupe(Value, decl_vals.items), + ), + 0, // default alignment + ); + return try Value.Tag.slice.create(sema.arena, .{ + .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), + .len = try Value.Tag.int_u64.create(sema.arena, decl_vals.items.len), + }); +} + +fn typeInfoNamespaceDecls( + sema: *Sema, + block: *Block, + decls_anon_decl: Allocator, + namespace: *Namespace, + decl_vals: *std.ArrayList(Value), + seen_namespaces: *std.AutoHashMap(*Namespace, void), +) !void { + const gop = try seen_namespaces.getOrPut(namespace); + if (gop.found_existing) return; + const decls = namespace.decls.keys(); + for (decls) |decl_index| { const decl = sema.mod.declPtr(decl_index); + if (decl.kind == .@"usingnamespace") { + try sema.mod.ensureDeclAnalyzed(decl_index); + var buf: Value.ToTypeBuffer = undefined; + const new_ns = decl.val.toType(&buf).getNamespace().?; + try sema.typeInfoNamespaceDecls(block, decls_anon_decl, new_ns, decl_vals, seen_namespaces); + continue; + } + if (decl.kind != .named) continue; const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); @@ -16231,37 +16274,21 @@ fn typeInfoDecls( try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), 0, // default alignment ); - break :v try Value.Tag.slice.create(decls_anon_decl.arena(), .{ - .ptr = try Value.Tag.decl_ref.create(decls_anon_decl.arena(), new_decl), - .len = try Value.Tag.int_u64.create(decls_anon_decl.arena(), bytes.len), + break :v try Value.Tag.slice.create(decls_anon_decl, .{ + .ptr = try Value.Tag.decl_ref.create(decls_anon_decl, new_decl), + .len = try Value.Tag.int_u64.create(decls_anon_decl, bytes.len), }); }; - const fields = try decls_anon_decl.arena().create([2]Value); + const fields = try decls_anon_decl.create([2]Value); fields.* = .{ //name: []const u8, name_val, //is_pub: bool, Value.makeBool(decl.is_pub), }; - decls_val.* = try Value.Tag.aggregate.create(decls_anon_decl.arena(), fields); + try decl_vals.append(try Value.Tag.aggregate.create(decls_anon_decl, fields)); } - - const new_decl = try decls_anon_decl.finish( - try Type.Tag.array.create(decls_anon_decl.arena(), .{ - .len = decls_vals.len, - .elem_type = declaration_ty, - }), - try Value.Tag.aggregate.create( - decls_anon_decl.arena(), - try decls_anon_decl.arena().dupe(Value, decls_vals), - ), - 0, // default alignment - ); - return try Value.Tag.slice.create(sema.arena, .{ - .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), - .len = try Value.Tag.int_u64.create(sema.arena, decls_vals.len), - }); } fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 0fb17519e6..ef8c89bd23 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -566,3 +566,27 @@ test "value from struct @typeInfo default_value can be loaded at comptime" { try expect(@ptrCast(*const u8, a).* == 1); } } + +test "@typeInfo decls and usingnamespace" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const A = struct { + const x = 5; + const y = 34; + + comptime {} + }; + const B = struct { + usingnamespace A; + const z = 56; + + test {} + }; + const decls = @typeInfo(B).Struct.decls; + try expect(decls.len == 3); + try expectEqualStrings(decls[0].name, "x"); + try expectEqualStrings(decls[1].name, "y"); + try expectEqualStrings(decls[2].name, "z"); +}