mirror of
https://github.com/ziglang/zig.git
synced 2024-11-26 23:22:44 +00:00
Move duplicate field detection for struct init expressions into AstGen
Partially addresses #17916.
This commit is contained in:
parent
acf9de376d
commit
941090d94f
@ -445,7 +445,7 @@ pub const ucontext_t = extern struct {
|
||||
sigmask: u64,
|
||||
mcontext: mcontext_t,
|
||||
stack: stack_t,
|
||||
sigmask: sigset_t,
|
||||
sigset: sigset_t,
|
||||
};
|
||||
|
||||
pub const rlimit_resource = enum(c_int) {
|
||||
|
101
src/AstGen.zig
101
src/AstGen.zig
@ -1722,6 +1722,57 @@ fn structInitExpr(
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var sfba = std.heap.stackFallback(256, astgen.arena);
|
||||
const sfba_allocator = sfba.get();
|
||||
|
||||
var duplicate_names = std.AutoArrayHashMap(u32, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
|
||||
defer duplicate_names.deinit();
|
||||
try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));
|
||||
|
||||
// When there aren't errors, use this to avoid a second iteration.
|
||||
var any_duplicate = false;
|
||||
|
||||
for (struct_init.ast.fields) |field| {
|
||||
const name_token = tree.firstToken(field) - 2;
|
||||
const name_index = try astgen.identAsString(name_token);
|
||||
|
||||
const gop = try duplicate_names.getOrPut(name_index);
|
||||
|
||||
if (gop.found_existing) {
|
||||
try gop.value_ptr.append(sfba_allocator, name_token);
|
||||
any_duplicate = true;
|
||||
} else {
|
||||
gop.value_ptr.* = .{};
|
||||
try gop.value_ptr.append(sfba_allocator, name_token);
|
||||
}
|
||||
}
|
||||
|
||||
if (any_duplicate) {
|
||||
var it = duplicate_names.iterator();
|
||||
|
||||
while (it.next()) |entry| {
|
||||
const record = entry.value_ptr.*;
|
||||
if (record.items.len > 1) {
|
||||
var error_notes = std.ArrayList(u32).init(astgen.arena);
|
||||
|
||||
for (record.items[1..]) |duplicate| {
|
||||
try error_notes.append(try astgen.errNoteTok(duplicate, "other field here", .{}));
|
||||
}
|
||||
|
||||
try astgen.appendErrorTokNotes(
|
||||
record.items[0],
|
||||
"duplicate field",
|
||||
.{},
|
||||
error_notes.items,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
}
|
||||
|
||||
if (struct_init.ast.type_expr != 0) {
|
||||
// Typed inits do not use RLS for language simplicity.
|
||||
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
|
||||
@ -4874,6 +4925,15 @@ fn structDeclInner(
|
||||
}
|
||||
};
|
||||
|
||||
var sfba = std.heap.stackFallback(256, astgen.arena);
|
||||
const sfba_allocator = sfba.get();
|
||||
|
||||
var duplicate_names = std.AutoArrayHashMap(u32, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
|
||||
try duplicate_names.ensureTotalCapacity(field_count);
|
||||
|
||||
// When there aren't errors, use this to avoid a second iteration.
|
||||
var any_duplicate = false;
|
||||
|
||||
var known_non_opv = false;
|
||||
var known_comptime_only = false;
|
||||
var any_comptime_fields = false;
|
||||
@ -4886,11 +4946,22 @@ fn structDeclInner(
|
||||
};
|
||||
|
||||
if (!is_tuple) {
|
||||
const field_name = try astgen.identAsString(member.ast.main_token);
|
||||
|
||||
member.convertToNonTupleLike(astgen.tree.nodes);
|
||||
assert(!member.ast.tuple_like);
|
||||
|
||||
const field_name = try astgen.identAsString(member.ast.main_token);
|
||||
wip_members.appendToField(field_name);
|
||||
|
||||
const gop = try duplicate_names.getOrPut(field_name);
|
||||
|
||||
if (gop.found_existing) {
|
||||
try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
|
||||
any_duplicate = true;
|
||||
} else {
|
||||
gop.value_ptr.* = .{};
|
||||
try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
|
||||
}
|
||||
} else if (!member.ast.tuple_like) {
|
||||
return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
|
||||
}
|
||||
@ -4975,6 +5046,34 @@ fn structDeclInner(
|
||||
}
|
||||
}
|
||||
|
||||
if (any_duplicate) {
|
||||
var it = duplicate_names.iterator();
|
||||
|
||||
while (it.next()) |entry| {
|
||||
const record = entry.value_ptr.*;
|
||||
if (record.items.len > 1) {
|
||||
var error_notes = std.ArrayList(u32).init(astgen.arena);
|
||||
|
||||
for (record.items[1..]) |duplicate| {
|
||||
try error_notes.append(try astgen.errNoteTok(duplicate, "other field here", .{}));
|
||||
}
|
||||
|
||||
try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
|
||||
|
||||
try astgen.appendErrorTokNotes(
|
||||
record.items[0],
|
||||
"duplicate struct field: '{s}'",
|
||||
.{try astgen.identifierTokenString(record.items[0])},
|
||||
error_notes.items,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
|
||||
duplicate_names.deinit();
|
||||
|
||||
try gz.setStruct(decl_inst, .{
|
||||
.src_node = node,
|
||||
.layout = layout,
|
||||
|
59
src/Sema.zig
59
src/Sema.zig
@ -4690,17 +4690,7 @@ fn validateStructInit(
|
||||
try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
|
||||
else
|
||||
try sema.structFieldIndex(block, struct_ty, field_name, field_src);
|
||||
if (found_fields[field_index.*].unwrap()) |other_field_ptr| {
|
||||
const other_field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(other_field_ptr)].pl_node;
|
||||
const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_ptr_data.src_node };
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
|
||||
errdefer msg.destroy(gpa);
|
||||
try sema.errNote(block, other_field_src, msg, "other field here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
assert(found_fields[field_index.*] == .none);
|
||||
found_fields[field_index.*] = field_ptr.toOptional();
|
||||
}
|
||||
|
||||
@ -19222,18 +19212,7 @@ fn zirStructInit(
|
||||
try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
|
||||
else
|
||||
try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
|
||||
if (field_inits[field_index] != .none) {
|
||||
const other_field_type = found_fields[field_index];
|
||||
const other_field_type_data = zir_datas[@intFromEnum(other_field_type)].pl_node;
|
||||
const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_type_data.src_node };
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
|
||||
errdefer msg.destroy(gpa);
|
||||
try sema.errNote(block, other_field_src, msg, "other field here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
assert(field_inits[field_index] == .none);
|
||||
found_fields[field_index] = item.data.field_type;
|
||||
const uncoerced_init = try sema.resolveInst(item.data.init);
|
||||
const field_ty = resolved_ty.structFieldType(field_index, mod);
|
||||
@ -19533,16 +19512,13 @@ fn structInitAnon(
|
||||
|
||||
const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
|
||||
const values = try sema.arena.alloc(InternPool.Index, types.len);
|
||||
|
||||
var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);
|
||||
try fields.ensureUnusedCapacity(types.len);
|
||||
const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len);
|
||||
|
||||
// Find which field forces the expression to be runtime, if any.
|
||||
const opt_runtime_index = rs: {
|
||||
var runtime_index: ?usize = null;
|
||||
var extra_index = extra_end;
|
||||
for (types, 0..) |*field_ty, i_usize| {
|
||||
const i: u32 = @intCast(i_usize);
|
||||
for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| {
|
||||
const item = switch (kind) {
|
||||
.anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
|
||||
.typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
|
||||
@ -19558,29 +19534,16 @@ fn structInitAnon(
|
||||
break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
|
||||
},
|
||||
};
|
||||
const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
|
||||
const gop = fields.getOrPutAssumeCapacity(name_ip);
|
||||
if (gop.found_existing) {
|
||||
const msg = msg: {
|
||||
const decl = mod.declPtr(block.src_decl);
|
||||
const field_src = mod.initSrc(src.node_offset.x, decl, i);
|
||||
const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
|
||||
errdefer msg.destroy(gpa);
|
||||
|
||||
const prev_source = mod.initSrc(src.node_offset.x, 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.* = i;
|
||||
const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
|
||||
field_name.* = name_ip;
|
||||
|
||||
const init = try sema.resolveInst(item.data.init);
|
||||
field_ty.* = sema.typeOf(init).toIntern();
|
||||
if (field_ty.toType().zigTypeTag(mod) == .Opaque) {
|
||||
const msg = msg: {
|
||||
const decl = mod.declPtr(block.src_decl);
|
||||
const field_src = mod.initSrc(src.node_offset.x, decl, i);
|
||||
const field_src = mod.initSrc(src.node_offset.x, decl, @intCast(i_usize));
|
||||
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);
|
||||
|
||||
@ -19590,17 +19553,17 @@ fn structInitAnon(
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
if (try sema.resolveValue(init)) |init_val| {
|
||||
values[i] = try init_val.intern(field_ty.toType(), mod);
|
||||
field_val.* = try init_val.intern(field_ty.toType(), mod);
|
||||
} else {
|
||||
values[i] = .none;
|
||||
runtime_index = i;
|
||||
field_val.* = .none;
|
||||
runtime_index = @intCast(i_usize);
|
||||
}
|
||||
}
|
||||
break :rs runtime_index;
|
||||
};
|
||||
|
||||
const tuple_ty = try ip.getAnonStructType(gpa, .{
|
||||
.names = fields.keys(),
|
||||
.names = names,
|
||||
.types = types,
|
||||
.values = values,
|
||||
});
|
||||
|
@ -14,5 +14,5 @@ export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :7:16: error: duplicate field
|
||||
// :4:16: note: other field here
|
||||
// :4:14: error: duplicate field
|
||||
// :7:14: note: other field here
|
||||
|
@ -6,5 +6,5 @@ pub export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:21: error: duplicate field
|
||||
// :2:13: note: other field here
|
||||
// :2:13: error: duplicate field
|
||||
// :2:21: note: other field here
|
||||
|
@ -17,5 +17,5 @@ export fn f() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :11:10: error: duplicate field
|
||||
// :8:10: note: other field here
|
||||
// :8:10: error: duplicate field
|
||||
// :11:10: note: other field here
|
||||
|
@ -2,15 +2,32 @@ const Foo = struct {
|
||||
Bar: i32,
|
||||
Bar: usize,
|
||||
};
|
||||
export fn entry() void {
|
||||
const a: Foo = undefined;
|
||||
_ = a;
|
||||
|
||||
const S = struct {
|
||||
a: u32,
|
||||
b: u32,
|
||||
a: u32,
|
||||
a: u64,
|
||||
};
|
||||
|
||||
export fn a() void {
|
||||
const f: Foo = undefined;
|
||||
_ = f;
|
||||
}
|
||||
|
||||
export fn b() void {
|
||||
const s: S = undefined;
|
||||
_ = s;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:5: error: duplicate struct field: 'Bar'
|
||||
// :2:5: note: other field here
|
||||
// :2:5: error: duplicate struct field: 'Bar'
|
||||
// :3:5: note: other field here
|
||||
// :1:13: note: struct declared here
|
||||
// :7:5: error: duplicate struct field: 'a'
|
||||
// :9:5: note: other field here
|
||||
// :10:5: note: other field here
|
||||
// :6:11: note: struct declared here
|
||||
|
@ -11,6 +11,6 @@ pub export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:9: error: duplicate struct field: 'e'
|
||||
// :3:9: note: other field here
|
||||
// :3:9: error: duplicate struct field: 'e'
|
||||
// :4:9: note: other field here
|
||||
// :2:22: note: struct declared here
|
||||
|
@ -11,6 +11,6 @@ export fn entry() void {
|
||||
// error
|
||||
// target=native
|
||||
//
|
||||
// :3:5: error: duplicate struct field: 'foo'
|
||||
// :2:5: note: other field here
|
||||
// :2:5: error: duplicate struct field: 'foo'
|
||||
// :3:5: note: other field here
|
||||
// :1:11: note: struct declared here
|
||||
|
@ -507,8 +507,8 @@ pub fn addCases(ctx: *Cases) !void {
|
||||
\\ return p.y - p.x - p.x;
|
||||
\\}
|
||||
, &.{
|
||||
":6:10: error: duplicate field",
|
||||
":4:10: note: other field here",
|
||||
":4:10: error: duplicate field",
|
||||
":6:10: note: other field here",
|
||||
});
|
||||
case.addError(
|
||||
\\const Point = struct { x: i32, y: i32 };
|
||||
|
Loading…
Reference in New Issue
Block a user