aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2022-07-22 18:21:03 +0300
committerVeikka Tuominen <git@vexu.eu>2022-07-23 15:40:12 +0300
commit2436dd2c1b976f5902be6d20a93c164631ce3df5 (patch)
tree2e8efabee27cfadf226dce06dae7e2b8ddbf0a6f /src
parent5b29275240a57448514fe5c5d9e8edae3b2362cc (diff)
downloadzig-2436dd2c1b976f5902be6d20a93c164631ce3df5.tar.gz
zig-2436dd2c1b976f5902be6d20a93c164631ce3df5.zip
Sema: validate duplicate fields in anon structs
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig7
-rw-r--r--src/Module.zig52
-rw-r--r--src/Sema.zig50
3 files changed, 94 insertions, 15 deletions
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();