diff --git a/src/AstGen.zig b/src/AstGen.zig index 6df27d6983..df1fb91567 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1589,13 +1589,12 @@ fn structInitExpr( switch (rl) { .discard => { - // TODO if a type expr is given the fields should be validated for that type if (struct_init.ast.type_expr != 0) { const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); - } - for (struct_init.ast.fields) |field_init| { - _ = try expr(gz, scope, .discard, field_init); + _ = try structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); + } else { + _ = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); } return Zir.Inst.Ref.void_value; }, diff --git a/src/Module.zig b/src/Module.zig index 9c4ae69fe6..8f7c42b542 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -5952,6 +5952,58 @@ pub fn argSrc( return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full.ast.params[arg_i])); } +pub fn initSrc( + init_node_offset: i32, + gpa: Allocator, + decl: *Decl, + init_index: usize, +) LazySrcLoc { + @setCold(true); + const tree = decl.getFileScope().getTree(gpa) catch |err| { + // In this case we emit a warning + a less precise source location. + log.warn("unable to load {s}: {s}", .{ + decl.getFileScope().sub_file_path, @errorName(err), + }); + return LazySrcLoc.nodeOffset(0); + }; + const node_tags = tree.nodes.items(.tag); + const node = decl.relativeToNodeIndex(init_node_offset); + var buf: [2]Ast.Node.Index = undefined; + const full = switch (node_tags[node]) { + .array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], node).ast.elements, + .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, node).ast.elements, + .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node).ast.elements, + .array_init, .array_init_comma => tree.arrayInit(node).ast.elements, + + .struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], node).ast.fields, + .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node).ast.fields, + .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node).ast.fields, + .struct_init, .struct_init_comma => tree.structInit(node).ast.fields, + else => unreachable, + }; + switch (node_tags[node]) { + .array_init_one, + .array_init_one_comma, + .array_init_dot_two, + .array_init_dot_two_comma, + .array_init_dot, + .array_init_dot_comma, + .array_init, + .array_init_comma, + => return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full[init_index])), + .struct_init_one, + .struct_init_one_comma, + .struct_init_dot_two, + .struct_init_dot_two_comma, + .struct_init_dot, + .struct_init_dot_comma, + .struct_init, + .struct_init_comma, + => return LazySrcLoc{ .node_offset_initializer = decl.nodeIndexToRelative(full[init_index]) }, + else => unreachable, + } +} + /// Called from `performAllTheWork`, after all AstGen workers have finished, /// and before the main semantic analysis loop begins. pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { diff --git a/src/Sema.zig b/src/Sema.zig index b2ba53c3e1..d23487a5fd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14721,22 +14721,41 @@ fn zirStructInitAnon( const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); const types = try sema.arena.alloc(Type, extra.data.fields_len); const values = try sema.arena.alloc(Value, types.len); - const names = try sema.arena.alloc([]const u8, types.len); + var fields = std.StringArrayHashMapUnmanaged(u32){}; + defer fields.deinit(sema.gpa); + try fields.ensureUnusedCapacity(sema.gpa, types.len); - const opt_runtime_src = rs: { - var runtime_src: ?LazySrcLoc = null; + const opt_runtime_index = rs: { + var runtime_index: ?usize = null; var extra_index = extra.end; for (types) |*field_ty, i| { - const init_src = src; // TODO better source location const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; - names[i] = sema.code.nullTerminatedString(item.data.field_name); + const name = sema.code.nullTerminatedString(item.data.field_name); + const gop = fields.getOrPutAssumeCapacity(name); + if (gop.found_existing) { + const msg = msg: { + const decl = sema.mod.declPtr(block.src_decl); + const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i); + const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); + errdefer msg.destroy(sema.gpa); + + const prev_source = Module.initSrc(src.node_offset.x, sema.gpa, decl, gop.value_ptr.*); + try sema.errNote(block, prev_source, msg, "other field here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + gop.value_ptr.* = @intCast(u32, i); + const init = try sema.resolveInst(item.data.init); field_ty.* = sema.typeOf(init); if (types[i].zigTypeTag() == .Opaque) { const msg = msg: { - const msg = try sema.errMsg(block, init_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); + const decl = sema.mod.declPtr(block.src_decl); + const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i); + const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, types[i]); @@ -14744,28 +14763,37 @@ fn zirStructInitAnon( }; return sema.failWithOwnedErrorMsg(block, msg); } + const init_src = src; // TODO better source location if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| { values[i] = init_val; } else { values[i] = Value.initTag(.unreachable_value); - runtime_src = init_src; + runtime_index = i; } } - break :rs runtime_src; + break :rs runtime_index; }; const tuple_ty = try Type.Tag.anon_struct.create(sema.arena, .{ - .names = names, + .names = try sema.arena.dupe([]const u8, fields.keys()), .types = types, .values = values, }); - const runtime_src = opt_runtime_src orelse { + const runtime_index = opt_runtime_index orelse { const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); }; - try sema.requireRuntimeBlock(block, src, runtime_src); + sema.requireRuntimeBlock(block, src, .unneeded) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, runtime_index); + try sema.requireRuntimeBlock(block, src, field_src); + return error.AnalysisFail; + }, + else => |e| return e, + }; if (is_ref) { const target = sema.mod.getTarget(); diff --git a/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig index 5cfce8e61e..3be57ac491 100644 --- a/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig +++ b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig @@ -34,5 +34,5 @@ export fn d() void { // :7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions // :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs // :18:22: note: opaque declared here -// :24:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs +// :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs // :23:22: note: opaque declared here diff --git a/test/cases/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig b/test/cases/compile_errors/duplicate_field_in_anonymous_struct_literal.zig similarity index 66% rename from test/cases/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig rename to test/cases/compile_errors/duplicate_field_in_anonymous_struct_literal.zig index 7b6821d669..ed153c1542 100644 --- a/test/cases/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig +++ b/test/cases/compile_errors/duplicate_field_in_anonymous_struct_literal.zig @@ -11,9 +11,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:7:13: error: duplicate field -// tmp.zig:4:13: note: other field here +// :7:16: error: duplicate field +// :4:16: note: other field here diff --git a/test/cases/compile_errors/invalid_store_to_comptime_field.zig b/test/cases/compile_errors/invalid_store_to_comptime_field.zig index 63d83c01d1..7fdb08facb 100644 --- a/test/cases/compile_errors/invalid_store_to_comptime_field.zig +++ b/test/cases/compile_errors/invalid_store_to_comptime_field.zig @@ -58,4 +58,5 @@ pub export fn entry4() void { // :14:19: error: value stored in comptime field does not match the default value of the field // :19:38: error: value stored in comptime field does not match the default value of the field // :31:19: error: value stored in comptime field does not match the default value of the field -// :41:14: error: value stored in comptime field does not match the default value of the field +// :25:29: note: default value set here +// :41:16: error: value stored in comptime field does not match the default value of the field