mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32: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,
|
sigmask: u64,
|
||||||
mcontext: mcontext_t,
|
mcontext: mcontext_t,
|
||||||
stack: stack_t,
|
stack: stack_t,
|
||||||
sigmask: sigset_t,
|
sigset: sigset_t,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const rlimit_resource = enum(c_int) {
|
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) {
|
if (struct_init.ast.type_expr != 0) {
|
||||||
// Typed inits do not use RLS for language simplicity.
|
// Typed inits do not use RLS for language simplicity.
|
||||||
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
|
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_non_opv = false;
|
||||||
var known_comptime_only = false;
|
var known_comptime_only = false;
|
||||||
var any_comptime_fields = false;
|
var any_comptime_fields = false;
|
||||||
@ -4886,11 +4946,22 @@ fn structDeclInner(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!is_tuple) {
|
if (!is_tuple) {
|
||||||
|
const field_name = try astgen.identAsString(member.ast.main_token);
|
||||||
|
|
||||||
member.convertToNonTupleLike(astgen.tree.nodes);
|
member.convertToNonTupleLike(astgen.tree.nodes);
|
||||||
assert(!member.ast.tuple_like);
|
assert(!member.ast.tuple_like);
|
||||||
|
|
||||||
const field_name = try astgen.identAsString(member.ast.main_token);
|
|
||||||
wip_members.appendToField(field_name);
|
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) {
|
} else if (!member.ast.tuple_like) {
|
||||||
return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
|
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, .{
|
try gz.setStruct(decl_inst, .{
|
||||||
.src_node = node,
|
.src_node = node,
|
||||||
.layout = layout,
|
.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)
|
try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
|
||||||
else
|
else
|
||||||
try sema.structFieldIndex(block, struct_ty, field_name, field_src);
|
try sema.structFieldIndex(block, struct_ty, field_name, field_src);
|
||||||
if (found_fields[field_index.*].unwrap()) |other_field_ptr| {
|
assert(found_fields[field_index.*] == .none);
|
||||||
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);
|
|
||||||
}
|
|
||||||
found_fields[field_index.*] = field_ptr.toOptional();
|
found_fields[field_index.*] = field_ptr.toOptional();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19222,18 +19212,7 @@ fn zirStructInit(
|
|||||||
try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
|
try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
|
||||||
else
|
else
|
||||||
try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
|
try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
|
||||||
if (field_inits[field_index] != .none) {
|
assert(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);
|
|
||||||
}
|
|
||||||
found_fields[field_index] = item.data.field_type;
|
found_fields[field_index] = item.data.field_type;
|
||||||
const uncoerced_init = try sema.resolveInst(item.data.init);
|
const uncoerced_init = try sema.resolveInst(item.data.init);
|
||||||
const field_ty = resolved_ty.structFieldType(field_index, mod);
|
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 types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
|
||||||
const values = try sema.arena.alloc(InternPool.Index, types.len);
|
const values = try sema.arena.alloc(InternPool.Index, types.len);
|
||||||
|
const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len);
|
||||||
var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);
|
|
||||||
try fields.ensureUnusedCapacity(types.len);
|
|
||||||
|
|
||||||
// Find which field forces the expression to be runtime, if any.
|
// Find which field forces the expression to be runtime, if any.
|
||||||
const opt_runtime_index = rs: {
|
const opt_runtime_index = rs: {
|
||||||
var runtime_index: ?usize = null;
|
var runtime_index: ?usize = null;
|
||||||
var extra_index = extra_end;
|
var extra_index = extra_end;
|
||||||
for (types, 0..) |*field_ty, i_usize| {
|
for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| {
|
||||||
const i: u32 = @intCast(i_usize);
|
|
||||||
const item = switch (kind) {
|
const item = switch (kind) {
|
||||||
.anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
|
.anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
|
||||||
.typed_init => sema.code.extraData(Zir.Inst.StructInit.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);
|
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.*);
|
const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
|
||||||
try sema.errNote(block, prev_source, msg, "other field here", .{});
|
field_name.* = name_ip;
|
||||||
break :msg msg;
|
|
||||||
};
|
|
||||||
return sema.failWithOwnedErrorMsg(block, msg);
|
|
||||||
}
|
|
||||||
gop.value_ptr.* = i;
|
|
||||||
|
|
||||||
const init = try sema.resolveInst(item.data.init);
|
const init = try sema.resolveInst(item.data.init);
|
||||||
field_ty.* = sema.typeOf(init).toIntern();
|
field_ty.* = sema.typeOf(init).toIntern();
|
||||||
if (field_ty.toType().zigTypeTag(mod) == .Opaque) {
|
if (field_ty.toType().zigTypeTag(mod) == .Opaque) {
|
||||||
const msg = msg: {
|
const msg = msg: {
|
||||||
const decl = mod.declPtr(block.src_decl);
|
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", .{});
|
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);
|
errdefer msg.destroy(sema.gpa);
|
||||||
|
|
||||||
@ -19590,17 +19553,17 @@ fn structInitAnon(
|
|||||||
return sema.failWithOwnedErrorMsg(block, msg);
|
return sema.failWithOwnedErrorMsg(block, msg);
|
||||||
}
|
}
|
||||||
if (try sema.resolveValue(init)) |init_val| {
|
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 {
|
} else {
|
||||||
values[i] = .none;
|
field_val.* = .none;
|
||||||
runtime_index = i;
|
runtime_index = @intCast(i_usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break :rs runtime_index;
|
break :rs runtime_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
const tuple_ty = try ip.getAnonStructType(gpa, .{
|
const tuple_ty = try ip.getAnonStructType(gpa, .{
|
||||||
.names = fields.keys(),
|
.names = names,
|
||||||
.types = types,
|
.types = types,
|
||||||
.values = values,
|
.values = values,
|
||||||
});
|
});
|
||||||
|
@ -14,5 +14,5 @@ export fn entry() void {
|
|||||||
// backend=stage2
|
// backend=stage2
|
||||||
// target=native
|
// target=native
|
||||||
//
|
//
|
||||||
// :7:16: error: duplicate field
|
// :4:14: error: duplicate field
|
||||||
// :4:16: note: other field here
|
// :7:14: note: other field here
|
||||||
|
@ -6,5 +6,5 @@ pub export fn entry() void {
|
|||||||
// backend=stage2
|
// backend=stage2
|
||||||
// target=native
|
// target=native
|
||||||
//
|
//
|
||||||
// :2:21: error: duplicate field
|
// :2:13: error: duplicate field
|
||||||
// :2:13: note: other field here
|
// :2:21: note: other field here
|
||||||
|
@ -17,5 +17,5 @@ export fn f() void {
|
|||||||
// backend=stage2
|
// backend=stage2
|
||||||
// target=native
|
// target=native
|
||||||
//
|
//
|
||||||
// :11:10: error: duplicate field
|
// :8:10: error: duplicate field
|
||||||
// :8:10: note: other field here
|
// :11:10: note: other field here
|
||||||
|
@ -2,15 +2,32 @@ const Foo = struct {
|
|||||||
Bar: i32,
|
Bar: i32,
|
||||||
Bar: usize,
|
Bar: usize,
|
||||||
};
|
};
|
||||||
export fn entry() void {
|
|
||||||
const a: Foo = undefined;
|
const S = struct {
|
||||||
_ = a;
|
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
|
// error
|
||||||
// backend=stage2
|
// backend=stage2
|
||||||
// target=native
|
// target=native
|
||||||
//
|
//
|
||||||
// :3:5: error: duplicate struct field: 'Bar'
|
// :2:5: error: duplicate struct field: 'Bar'
|
||||||
// :2:5: note: other field here
|
// :3:5: note: other field here
|
||||||
// :1:13: note: struct declared 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
|
// backend=stage2
|
||||||
// target=native
|
// target=native
|
||||||
//
|
//
|
||||||
// :4:9: error: duplicate struct field: 'e'
|
// :3:9: error: duplicate struct field: 'e'
|
||||||
// :3:9: note: other field here
|
// :4:9: note: other field here
|
||||||
// :2:22: note: struct declared here
|
// :2:22: note: struct declared here
|
||||||
|
@ -11,6 +11,6 @@ export fn entry() void {
|
|||||||
// error
|
// error
|
||||||
// target=native
|
// target=native
|
||||||
//
|
//
|
||||||
// :3:5: error: duplicate struct field: 'foo'
|
// :2:5: error: duplicate struct field: 'foo'
|
||||||
// :2:5: note: other field here
|
// :3:5: note: other field here
|
||||||
// :1:11: note: struct declared here
|
// :1:11: note: struct declared here
|
||||||
|
@ -507,8 +507,8 @@ pub fn addCases(ctx: *Cases) !void {
|
|||||||
\\ return p.y - p.x - p.x;
|
\\ return p.y - p.x - p.x;
|
||||||
\\}
|
\\}
|
||||||
, &.{
|
, &.{
|
||||||
":6:10: error: duplicate field",
|
":4:10: error: duplicate field",
|
||||||
":4:10: note: other field here",
|
":6:10: note: other field here",
|
||||||
});
|
});
|
||||||
case.addError(
|
case.addError(
|
||||||
\\const Point = struct { x: i32, y: i32 };
|
\\const Point = struct { x: i32, y: i32 };
|
||||||
|
Loading…
Reference in New Issue
Block a user