mirror of
https://github.com/ziglang/zig.git
synced 2026-02-16 14:28:57 +00:00
Sema: validate duplicate fields in anon structs
This commit is contained in:
parent
5b29275240
commit
2436dd2c1b
@ -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;
|
||||
},
|
||||
|
||||
@ -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 {
|
||||
|
||||
50
src/Sema.zig
50
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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user