| author | |
| committer | |
| log | 2436dd2c1b976f5902be6d20a93c164631ce3df5 |
| tree | 2e8efabee27cfadf226dce06dae7e2b8ddbf0a6f |
| parent | 5b29275240a57448514fe5c5d9e8edae3b2362cc |
7 files changed, 115 insertions(+), 36 deletions(-)
src/AstGen.zig+3-4| ... | @@ -1589,13 +1589,12 @@ fn structInitExpr( | ... | @@ -1589,13 +1589,12 @@ fn structInitExpr( |
| 1589 | 1589 | ||
| 1590 | switch (rl) { | 1590 | switch (rl) { |
| 1591 | .discard => { | 1591 | .discard => { |
| 1592 | // TODO if a type expr is given the fields should be validated for that type | ||
| 1593 | if (struct_init.ast.type_expr != 0) { | 1592 | if (struct_init.ast.type_expr != 0) { |
| 1594 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); | 1593 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); |
| 1595 | _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); | 1594 | _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); |
| 1596 | } | 1595 | _ = try structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); |
| 1597 | for (struct_init.ast.fields) |field_init| { | 1596 | } else { |
| 1598 | _ = try expr(gz, scope, .discard, field_init); | 1597 | _ = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); |
| 1599 | } | 1598 | } |
| 1600 | return Zir.Inst.Ref.void_value; | 1599 | return Zir.Inst.Ref.void_value; |
| 1601 | }, | 1600 | }, |
src/Module.zig+52| ... | @@ -5952,6 +5952,58 @@ pub fn argSrc( | ... | @@ -5952,6 +5952,58 @@ pub fn argSrc( |
| 5952 | return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full.ast.params[arg_i])); | 5952 | return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full.ast.params[arg_i])); |
| 5953 | } | 5953 | } |
| 5954 | 5954 | ||
| 5955 | pub fn initSrc( | ||
| 5956 | init_node_offset: i32, | ||
| 5957 | gpa: Allocator, | ||
| 5958 | decl: *Decl, | ||
| 5959 | init_index: usize, | ||
| 5960 | ) LazySrcLoc { | ||
| 5961 | @setCold(true); | ||
| 5962 | const tree = decl.getFileScope().getTree(gpa) catch |err| { | ||
| 5963 | // In this case we emit a warning + a less precise source location. | ||
| 5964 | log.warn("unable to load {s}: {s}", .{ | ||
| 5965 | decl.getFileScope().sub_file_path, @errorName(err), | ||
| 5966 | }); | ||
| 5967 | return LazySrcLoc.nodeOffset(0); | ||
| 5968 | }; | ||
| 5969 | const node_tags = tree.nodes.items(.tag); | ||
| 5970 | const node = decl.relativeToNodeIndex(init_node_offset); | ||
| 5971 | var buf: [2]Ast.Node.Index = undefined; | ||
| 5972 | const full = switch (node_tags[node]) { | ||
| 5973 | .array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], node).ast.elements, | ||
| 5974 | .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, node).ast.elements, | ||
| 5975 | .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node).ast.elements, | ||
| 5976 | .array_init, .array_init_comma => tree.arrayInit(node).ast.elements, | ||
| 5977 | |||
| 5978 | .struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], node).ast.fields, | ||
| 5979 | .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node).ast.fields, | ||
| 5980 | .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node).ast.fields, | ||
| 5981 | .struct_init, .struct_init_comma => tree.structInit(node).ast.fields, | ||
| 5982 | else => unreachable, | ||
| 5983 | }; | ||
| 5984 | switch (node_tags[node]) { | ||
| 5985 | .array_init_one, | ||
| 5986 | .array_init_one_comma, | ||
| 5987 | .array_init_dot_two, | ||
| 5988 | .array_init_dot_two_comma, | ||
| 5989 | .array_init_dot, | ||
| 5990 | .array_init_dot_comma, | ||
| 5991 | .array_init, | ||
| 5992 | .array_init_comma, | ||
| 5993 | => return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full[init_index])), | ||
| 5994 | .struct_init_one, | ||
| 5995 | .struct_init_one_comma, | ||
| 5996 | .struct_init_dot_two, | ||
| 5997 | .struct_init_dot_two_comma, | ||
| 5998 | .struct_init_dot, | ||
| 5999 | .struct_init_dot_comma, | ||
| 6000 | .struct_init, | ||
| 6001 | .struct_init_comma, | ||
| 6002 | => return LazySrcLoc{ .node_offset_initializer = decl.nodeIndexToRelative(full[init_index]) }, | ||
| 6003 | else => unreachable, | ||
| 6004 | } | ||
| 6005 | } | ||
| 6006 | |||
| 5955 | /// Called from `performAllTheWork`, after all AstGen workers have finished, | 6007 | /// Called from `performAllTheWork`, after all AstGen workers have finished, |
| 5956 | /// and before the main semantic analysis loop begins. | 6008 | /// and before the main semantic analysis loop begins. |
| 5957 | pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { | 6009 | pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { |
src/Sema.zig+39-11| ... | @@ -14721,22 +14721,41 @@ fn zirStructInitAnon( | ... | @@ -14721,22 +14721,41 @@ fn zirStructInitAnon( |
| 14721 | const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); | 14721 | const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); |
| 14722 | const types = try sema.arena.alloc(Type, extra.data.fields_len); | 14722 | const types = try sema.arena.alloc(Type, extra.data.fields_len); |
| 14723 | const values = try sema.arena.alloc(Value, types.len); | 14723 | const values = try sema.arena.alloc(Value, types.len); |
| 14724 | const names = try sema.arena.alloc([]const u8, types.len); | 14724 | var fields = std.StringArrayHashMapUnmanaged(u32){}; |
| 14725 | defer fields.deinit(sema.gpa); | ||
| 14726 | try fields.ensureUnusedCapacity(sema.gpa, types.len); | ||
| 14725 | 14727 | ||
| 14726 | const opt_runtime_src = rs: { | 14728 | const opt_runtime_index = rs: { |
| 14727 | var runtime_src: ?LazySrcLoc = null; | 14729 | var runtime_index: ?usize = null; |
| 14728 | var extra_index = extra.end; | 14730 | var extra_index = extra.end; |
| 14729 | for (types) |*field_ty, i| { | 14731 | for (types) |*field_ty, i| { |
| 14730 | const init_src = src; // TODO better source location | ||
| 14731 | const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); | 14732 | const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); |
| 14732 | extra_index = item.end; | 14733 | extra_index = item.end; |
| 14733 | 14734 | ||
| 14734 | names[i] = sema.code.nullTerminatedString(item.data.field_name); | 14735 | const name = sema.code.nullTerminatedString(item.data.field_name); |
| 14736 | const gop = fields.getOrPutAssumeCapacity(name); | ||
| 14737 | if (gop.found_existing) { | ||
| 14738 | const msg = msg: { | ||
| 14739 | const decl = sema.mod.declPtr(block.src_decl); | ||
| 14740 | const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i); | ||
| 14741 | const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); | ||
| 14742 | errdefer msg.destroy(sema.gpa); | ||
| 14743 | |||
| 14744 | const prev_source = Module.initSrc(src.node_offset.x, sema.gpa, decl, gop.value_ptr.*); | ||
| 14745 | try sema.errNote(block, prev_source, msg, "other field here", .{}); | ||
| 14746 | break :msg msg; | ||
| 14747 | }; | ||
| 14748 | return sema.failWithOwnedErrorMsg(block, msg); | ||
| 14749 | } | ||
| 14750 | gop.value_ptr.* = @intCast(u32, i); | ||
| 14751 | |||
| 14735 | const init = try sema.resolveInst(item.data.init); | 14752 | const init = try sema.resolveInst(item.data.init); |
| 14736 | field_ty.* = sema.typeOf(init); | 14753 | field_ty.* = sema.typeOf(init); |
| 14737 | if (types[i].zigTypeTag() == .Opaque) { | 14754 | if (types[i].zigTypeTag() == .Opaque) { |
| 14738 | const msg = msg: { | 14755 | const msg = msg: { |
| 14739 | const msg = try sema.errMsg(block, init_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); | 14756 | const decl = sema.mod.declPtr(block.src_decl); |
| 14757 | const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i); | ||
| 14758 | const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); | ||
| 14740 | errdefer msg.destroy(sema.gpa); | 14759 | errdefer msg.destroy(sema.gpa); |
| 14741 | 14760 | ||
| 14742 | try sema.addDeclaredHereNote(msg, types[i]); | 14761 | try sema.addDeclaredHereNote(msg, types[i]); |
| ... | @@ -14744,28 +14763,37 @@ fn zirStructInitAnon( | ... | @@ -14744,28 +14763,37 @@ fn zirStructInitAnon( |
| 14744 | }; | 14763 | }; |
| 14745 | return sema.failWithOwnedErrorMsg(block, msg); | 14764 | return sema.failWithOwnedErrorMsg(block, msg); |
| 14746 | } | 14765 | } |
| 14766 | const init_src = src; // TODO better source location | ||
| 14747 | if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| { | 14767 | if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| { |
| 14748 | values[i] = init_val; | 14768 | values[i] = init_val; |
| 14749 | } else { | 14769 | } else { |
| 14750 | values[i] = Value.initTag(.unreachable_value); | 14770 | values[i] = Value.initTag(.unreachable_value); |
| 14751 | runtime_src = init_src; | 14771 | runtime_index = i; |
| 14752 | } | 14772 | } |
| 14753 | } | 14773 | } |
| 14754 | break :rs runtime_src; | 14774 | break :rs runtime_index; |
| 14755 | }; | 14775 | }; |
| 14756 | 14776 | ||
| 14757 | const tuple_ty = try Type.Tag.anon_struct.create(sema.arena, .{ | 14777 | const tuple_ty = try Type.Tag.anon_struct.create(sema.arena, .{ |
| 14758 | .names = names, | 14778 | .names = try sema.arena.dupe([]const u8, fields.keys()), |
| 14759 | .types = types, | 14779 | .types = types, |
| 14760 | .values = values, | 14780 | .values = values, |
| 14761 | }); | 14781 | }); |
| 14762 | 14782 | ||
| 14763 | const runtime_src = opt_runtime_src orelse { | 14783 | const runtime_index = opt_runtime_index orelse { |
| 14764 | const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); | 14784 | const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); |
| 14765 | return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); | 14785 | return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); |
| 14766 | }; | 14786 | }; |
| 14767 | 14787 | ||
| 14768 | try sema.requireRuntimeBlock(block, src, runtime_src); | 14788 | sema.requireRuntimeBlock(block, src, .unneeded) catch |err| switch (err) { |
| 14789 | error.NeededSourceLocation => { | ||
| 14790 | const decl = sema.mod.declPtr(block.src_decl); | ||
| 14791 | const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, runtime_index); | ||
| 14792 | try sema.requireRuntimeBlock(block, src, field_src); | ||
| 14793 | return error.AnalysisFail; | ||
| 14794 | }, | ||
| 14795 | else => |e| return e, | ||
| 14796 | }; | ||
| 14769 | 14797 | ||
| 14770 | if (is_ref) { | 14798 | if (is_ref) { |
| 14771 | const target = sema.mod.getTarget(); | 14799 | const target = sema.mod.getTarget(); |
test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig+1-1| ... | @@ -34,5 +34,5 @@ export fn d() void { | ... | @@ -34,5 +34,5 @@ export fn d() void { |
| 34 | // :7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions | 34 | // :7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions |
| 35 | // :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs | 35 | // :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs |
| 36 | // :18:22: note: opaque declared here | 36 | // :18:22: note: opaque declared here |
| 37 | // :24:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs | 37 | // :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs |
| 38 | // :23:22: note: opaque declared here | 38 | // :23:22: note: opaque declared here |
test/cases/compile_errors/duplicate_field_in_anonymous_struct_literal.zig created+18| ... | @@ -0,0 +1,18 @@ | ||
| 1 | export fn entry() void { | ||
| 2 | const anon = .{ | ||
| 3 | .inner = .{ | ||
| 4 | .a = .{ | ||
| 5 | .something = "text", | ||
| 6 | }, | ||
| 7 | .a = .{}, | ||
| 8 | }, | ||
| 9 | }; | ||
| 10 | _ = anon; | ||
| 11 | } | ||
| 12 | |||
| 13 | // error | ||
| 14 | // backend=stage2 | ||
| 15 | // target=native | ||
| 16 | // | ||
| 17 | // :7:16: error: duplicate field | ||
| 18 | // :4:16: note: other field here | ||
test/cases/compile_errors/invalid_store_to_comptime_field.zig+2-1| ... | @@ -58,4 +58,5 @@ pub export fn entry4() void { | ... | @@ -58,4 +58,5 @@ pub export fn entry4() void { |
| 58 | // :14:19: error: value stored in comptime field does not match the default value of the field | 58 | // :14:19: error: value stored in comptime field does not match the default value of the field |
| 59 | // :19:38: error: value stored in comptime field does not match the default value of the field | 59 | // :19:38: error: value stored in comptime field does not match the default value of the field |
| 60 | // :31:19: error: value stored in comptime field does not match the default value of the field | 60 | // :31:19: error: value stored in comptime field does not match the default value of the field |
| 61 | // :41:14: error: value stored in comptime field does not match the default value of the field | 61 | // :25:29: note: default value set here |
| 62 | // :41:16: error: value stored in comptime field does not match the default value of the field |
test/cases/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig deleted-19| ... | @@ -1,19 +0,0 @@ | ||
| 1 | export fn entry() void { | ||
| 2 | const anon = .{ | ||
| 3 | .inner = .{ | ||
| 4 | .a = .{ | ||
| 5 | .something = "text", | ||
| 6 | }, | ||
| 7 | .a = .{}, | ||
| 8 | }, | ||
| 9 | }; | ||
| 10 | _ = anon; | ||
| 11 | } | ||
| 12 | |||
| 13 | // error | ||
| 14 | // backend=stage1 | ||
| 15 | // target=native | ||
| 16 | // is_test=1 | ||
| 17 | // | ||
| 18 | // tmp.zig:7:13: error: duplicate field | ||
| 19 | // tmp.zig:4:13: note: other field here | ||