authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-04-15 11:34:04+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-04-15 11:34:04+03:00
log3723eb7f3164c40c9cb204cb81efeb6ae3f43847
tree11fa521d0a6bff7ec373740dc394ebd575a29d13
parent62d717e2ffb1e9a1127652521de57c2e18cf7d3b
parent94fd914e584d466808f40b9eb5fac49c1cc3c66a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11242 from schmee/sema-handle-more-union-errors

stage2: add more union compile errors / improve error messages

9 files changed, 279 insertions(+), 16 deletions(-)

src/Sema.zig+114-16
......@@ -1589,6 +1589,21 @@ fn errNote(
15891589 return sema.mod.errNoteNonLazy(src.toSrcLoc(block.src_decl), parent, format, args);
15901590}
15911591
1592fn addFieldErrNote(
1593 sema: *Sema,
1594 block: *Block,
1595 container_ty: Type,
1596 field_index: usize,
1597 parent: *Module.ErrorMsg,
1598 comptime format: []const u8,
1599 args: anytype,
1600) !void {
1601 const decl = container_ty.getOwnerDecl();
1602 const tree = try sema.getAstTree(block);
1603 const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index);
1604 try sema.mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args);
1605}
1606
15921607fn errMsg(
15931608 sema: *Sema,
15941609 block: *Block,
......@@ -17573,9 +17588,15 @@ fn unionFieldVal(
1757317588 if (tag_matches) {
1757417589 return sema.addConstant(field.ty, tag_and_val.val);
1757517590 } else {
17576 // TODO enhance this saying which one was active
17577 // and which one was accessed, and showing where the union was declared.
17578 return sema.fail(block, src, "access of inactive union field", .{});
17591 const msg = msg: {
17592 const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data;
17593 const active_field_name = union_obj.fields.keys()[active_index];
17594 const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name });
17595 errdefer msg.destroy(sema.gpa);
17596 try sema.addDeclaredHereNote(msg, union_ty);
17597 break :msg msg;
17598 };
17599 return sema.failWithOwnedErrorMsg(block, msg);
1757917600 }
1758017601 },
1758117602 .Packed, .Extern => {
......@@ -19702,13 +19723,14 @@ fn coerceEnumToUnion(
1970219723 const field = union_obj.fields.values()[field_index];
1970319724 const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);
1970419725 const opv = (try sema.typeHasOnePossibleValue(block, inst_src, field_ty)) orelse {
19705 // TODO resolve the field names and include in the error message,
19706 // also instead of 'union declared here' make it 'field "foo" declared here'.
1970719726 const msg = msg: {
19708 const msg = try sema.errMsg(block, inst_src, "coercion to union {} must initialize {} field", .{
19709 union_ty.fmt(target), field_ty.fmt(target),
19727 const field_name = union_obj.fields.keys()[field_index];
19728 const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{s}'", .{
19729 inst_ty.fmt(target), union_ty.fmt(target), field_ty.fmt(target), field_name,
1971019730 });
1971119731 errdefer msg.destroy(sema.gpa);
19732
19733 try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name});
1971219734 try sema.addDeclaredHereNote(msg, union_ty);
1971319735 break :msg msg;
1971419736 };
......@@ -19740,13 +19762,24 @@ fn coerceEnumToUnion(
1974019762 return block.addBitCast(union_ty, enum_tag);
1974119763 }
1974219764
19743 // TODO resolve the field names and add a hint that says "field 'foo' has type 'bar'"
19744 // instead of the "union declared here" hint
1974519765 const msg = msg: {
19746 const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} which has non-void fields", .{
19747 union_ty.fmt(target),
19748 });
19766 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
19767 const msg = try sema.errMsg(
19768 block,
19769 inst_src,
19770 "runtime coercion from enum '{}' to union '{}' which has non-void fields",
19771 .{ tag_ty.fmt(target), union_ty.fmt(target) },
19772 );
1974919773 errdefer msg.destroy(sema.gpa);
19774
19775 var it = union_obj.fields.iterator();
19776 var field_index: usize = 0;
19777 while (it.next()) |field| {
19778 const field_name = field.key_ptr.*;
19779 const field_ty = field.value_ptr.ty;
19780 try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) });
19781 field_index += 1;
19782 }
1975019783 try sema.addDeclaredHereNote(msg, union_ty);
1975119784 break :msg msg;
1975219785 };
......@@ -21835,7 +21868,7 @@ fn resolveTypeFieldsUnion(
2183521868 }
2183621869
2183721870 union_obj.status = .field_types_wip;
21838 try semaUnionFields(sema.mod, union_obj);
21871 try semaUnionFields(block, sema.mod, union_obj);
2183921872 union_obj.status = .have_field_types;
2184021873}
2184121874
......@@ -22044,7 +22077,21 @@ fn semaStructFields(
2204422077 }
2204522078
2204622079 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
22047 assert(!gop.found_existing);
22080 if (gop.found_existing) {
22081 const msg = msg: {
22082 const tree = try sema.getAstTree(&block_scope);
22083 const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, field_i);
22084 const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name});
22085 errdefer msg.destroy(gpa);
22086
22087 const prev_field_index = struct_obj.fields.getIndex(field_name).?;
22088 const prev_field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, prev_field_index);
22089 try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
22090 try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
22091 break :msg msg;
22092 };
22093 return sema.failWithOwnedErrorMsg(&block_scope, msg);
22094 }
2204822095 gop.value_ptr.* = .{
2204922096 .ty = try field_ty.copy(decl_arena_allocator),
2205022097 .abi_align = 0,
......@@ -22075,7 +22122,7 @@ fn semaStructFields(
2207522122 }
2207622123}
2207722124
22078fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22125fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void {
2207922126 const tracy = trace(@src());
2208022127 defer tracy.end();
2208122128
......@@ -22175,6 +22222,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2217522222 var int_tag_ty: Type = undefined;
2217622223 var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
2217722224 var enum_value_map: ?*Module.EnumNumbered.ValueMap = null;
22225 var tag_ty_field_names: ?Module.EnumFull.NameMap = null;
2217822226 if (tag_type_ref != .none) {
2217922227 const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref);
2218022228 if (small.auto_enum_tag) {
......@@ -22187,6 +22235,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2218722235 } else {
2218822236 // The provided type is the enum tag type.
2218922237 union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator);
22238 // The fields of the union must match the enum exactly.
22239 // Store a copy of the enum field names so we can check for
22240 // missing or extraneous fields later.
22241 tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena);
2219022242 }
2219122243 } else {
2219222244 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
......@@ -22295,7 +22347,35 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2229522347 }
2229622348
2229722349 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
22298 assert(!gop.found_existing);
22350 if (gop.found_existing) {
22351 const msg = msg: {
22352 const tree = try sema.getAstTree(&block_scope);
22353 const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
22354 const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name});
22355 errdefer msg.destroy(gpa);
22356
22357 const prev_field_index = union_obj.fields.getIndex(field_name).?;
22358 const prev_field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, prev_field_index);
22359 try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
22360 try sema.errNote(&block_scope, src, msg, "union declared here", .{});
22361 break :msg msg;
22362 };
22363 return sema.failWithOwnedErrorMsg(&block_scope, msg);
22364 }
22365
22366 if (tag_ty_field_names) |*names| {
22367 const enum_has_field = names.orderedRemove(field_name);
22368 if (!enum_has_field) {
22369 const msg = msg: {
22370 const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name });
22371 errdefer msg.destroy(sema.gpa);
22372 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
22373 break :msg msg;
22374 };
22375 return sema.failWithOwnedErrorMsg(block, msg);
22376 }
22377 }
22378
2229922379 gop.value_ptr.* = .{
2230022380 .ty = try field_ty.copy(decl_arena_allocator),
2230122381 .abi_align = 0,
......@@ -22310,6 +22390,24 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2231022390 gop.value_ptr.abi_align = 0;
2231122391 }
2231222392 }
22393
22394 if (tag_ty_field_names) |names| {
22395 if (names.count() > 0) {
22396 const msg = msg: {
22397 const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
22398 errdefer msg.destroy(sema.gpa);
22399
22400 const enum_ty = union_obj.tag_ty;
22401 for (names.keys()) |field_name| {
22402 const field_index = enum_ty.enumFieldIndex(field_name).?;
22403 try sema.addFieldErrNote(block, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name});
22404 }
22405 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
22406 break :msg msg;
22407 };
22408 return sema.failWithOwnedErrorMsg(block, msg);
22409 }
22410 }
2231322411}
2231422412
2231522413fn generateUnionTagTypeNumbered(
src/type.zig+44
......@@ -5312,6 +5312,50 @@ pub const Type = extern union {
53125312 }
53135313 }
53145314
5315 pub fn getNodeOffset(ty: Type) i32 {
5316 switch (ty.tag()) {
5317 .enum_full, .enum_nonexhaustive => {
5318 const enum_full = ty.cast(Payload.EnumFull).?.data;
5319 return enum_full.node_offset;
5320 },
5321 .enum_numbered => return ty.castTag(.enum_numbered).?.data.node_offset,
5322 .enum_simple => {
5323 const enum_simple = ty.castTag(.enum_simple).?.data;
5324 return enum_simple.node_offset;
5325 },
5326 .@"struct" => {
5327 const struct_obj = ty.castTag(.@"struct").?.data;
5328 return struct_obj.node_offset;
5329 },
5330 .error_set => {
5331 const error_set = ty.castTag(.error_set).?.data;
5332 return error_set.node_offset;
5333 },
5334 .@"union", .union_tagged => {
5335 const union_obj = ty.cast(Payload.Union).?.data;
5336 return union_obj.node_offset;
5337 },
5338 .@"opaque" => {
5339 const opaque_obj = ty.cast(Payload.Opaque).?.data;
5340 return opaque_obj.node_offset;
5341 },
5342 .atomic_order,
5343 .atomic_rmw_op,
5344 .calling_convention,
5345 .address_space,
5346 .float_mode,
5347 .reduce_op,
5348 .call_options,
5349 .prefetch_options,
5350 .export_options,
5351 .extern_options,
5352 .type_info,
5353 => unreachable, // These need to be resolved earlier.
5354
5355 else => unreachable,
5356 }
5357 }
5358
53155359 /// Asserts the type is an enum.
53165360 pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {
53175361 const S = struct {
test/compile_errors/stage2/struct_duplicate_field_name.zig created+15
......@@ -0,0 +1,15 @@
1const S = struct {
2 foo: u32,
3 foo: u32,
4};
5
6export fn entry() void {
7 const s: S = .{ .foo = 100 };
8 _ = s;
9}
10
11// duplicate struct field name
12//
13// :3:5: error: duplicate struct field: 'foo'
14// :2:5: note: other field here
15// :1:11: note: struct declared here
test/compile_errors/stage2/union_access_of_inactive_field.zig created+14
......@@ -0,0 +1,14 @@
1const U = union {
2 a: void,
3 b: u64,
4};
5comptime {
6 var u: U = .{.a = {}};
7 const v = u.b;
8 _ = v;
9}
10
11// access of inactive union field
12//
13// :7:16: error: access of union field 'b' while field 'a' is active
14// :1:11: note: union declared here
test/compile_errors/stage2/union_duplicate_enum_field.zig created+16
......@@ -0,0 +1,16 @@
1const E = enum {a, b};
2const U = union(E) {
3 a: u32,
4 a: u32,
5};
6
7export fn foo() void {
8 var u: U = .{ .a = 123 };
9 _ = u;
10}
11
12// union with enum and duplicate fields
13//
14// :4:5: error: duplicate union field: 'a'
15// :3:5: note: other field here
16// :2:11: note: union declared here
test/compile_errors/stage2/union_duplicate_field_definition.zig created+15
......@@ -0,0 +1,15 @@
1const U = union {
2 foo: u32,
3 foo: u32,
4};
5
6export fn entry() void {
7 const u: U = .{ .foo = 100 };
8 _ = u;
9}
10
11// duplicate union field name
12//
13// :3:5: error: duplicate union field: 'foo'
14// :2:5: note: other field here
15// :1:11: note: union declared here
test/compile_errors/stage2/union_enum_field_missing.zig created+20
......@@ -0,0 +1,20 @@
1const E = enum {
2 a,
3 b,
4 c,
5};
6
7const U = union(E) {
8 a: i32,
9 b: f64,
10};
11
12export fn entry() usize {
13 return @sizeOf(U);
14}
15
16// enum field missing in union
17//
18// :7:1: error: enum field(s) missing in union
19// :4:5: note: field 'c' missing, declared here
20// :1:11: note: enum declared here
test/compile_errors/stage2/union_extra_field.zig created+19
......@@ -0,0 +1,19 @@
1const E = enum {
2 a,
3 b,
4 c,
5};
6const U = union(E) {
7 a: i32,
8 b: f64,
9 c: f64,
10 d: f64,
11};
12export fn entry() usize {
13 return @sizeOf(U);
14}
15
16// union extra field
17//
18// :6:1: error: enum 'tmp.E' has no field named 'd'
19// :1:11: note: enum declared here
test/compile_errors/stage2/union_runtime_coercion_from_enum.zig created+22
......@@ -0,0 +1,22 @@
1const E = enum {
2 a,
3 b,
4};
5const U = union(E) {
6 a: u32,
7 b: u64,
8};
9fn foo() E {
10 return E.b;
11}
12export fn doTheTest() u64 {
13 var u: U = foo();
14 return u.b;
15}
16
17// runtime coercion from enum to union
18//
19// :13:19: error: runtime coercion from enum 'tmp.E' to union 'tmp.U' which has non-void fields
20// :6:5: note: field 'a' has type 'u32'
21// :7:5: note: field 'b' has type 'u64'
22// :5:11: note: union declared here