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(...@@ -1589,6 +1589,21 @@ fn errNote(
1589 return sema.mod.errNoteNonLazy(src.toSrcLoc(block.src_decl), parent, format, args);1589 return sema.mod.errNoteNonLazy(src.toSrcLoc(block.src_decl), parent, format, args);
1590}1590}
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
1592fn errMsg(1607fn errMsg(
1593 sema: *Sema,1608 sema: *Sema,
1594 block: *Block,1609 block: *Block,
...@@ -17573,9 +17588,15 @@ fn unionFieldVal(...@@ -17573,9 +17588,15 @@ fn unionFieldVal(
17573 if (tag_matches) {17588 if (tag_matches) {
17574 return sema.addConstant(field.ty, tag_and_val.val);17589 return sema.addConstant(field.ty, tag_and_val.val);
17575 } else {17590 } else {
17576 // TODO enhance this saying which one was active17591 const msg = msg: {
17577 // and which one was accessed, and showing where the union was declared.17592 const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data;
17578 return sema.fail(block, src, "access of inactive union field", .{});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);
17579 }17600 }
17580 },17601 },
17581 .Packed, .Extern => {17602 .Packed, .Extern => {
...@@ -19702,13 +19723,14 @@ fn coerceEnumToUnion(...@@ -19702,13 +19723,14 @@ fn coerceEnumToUnion(
19702 const field = union_obj.fields.values()[field_index];19723 const field = union_obj.fields.values()[field_index];
19703 const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);19724 const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);
19704 const opv = (try sema.typeHasOnePossibleValue(block, inst_src, field_ty)) orelse {19725 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'.
19707 const msg = msg: {19726 const msg = msg: {
19708 const msg = try sema.errMsg(block, inst_src, "coercion to union {} must initialize {} field", .{19727 const field_name = union_obj.fields.keys()[field_index];
19709 union_ty.fmt(target), field_ty.fmt(target),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,
19710 });19730 });
19711 errdefer msg.destroy(sema.gpa);19731 errdefer msg.destroy(sema.gpa);
19732
19733 try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name});
19712 try sema.addDeclaredHereNote(msg, union_ty);19734 try sema.addDeclaredHereNote(msg, union_ty);
19713 break :msg msg;19735 break :msg msg;
19714 };19736 };
...@@ -19740,13 +19762,24 @@ fn coerceEnumToUnion(...@@ -19740,13 +19762,24 @@ fn coerceEnumToUnion(
19740 return block.addBitCast(union_ty, enum_tag);19762 return block.addBitCast(union_ty, enum_tag);
19741 }19763 }
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
19745 const msg = msg: {19765 const msg = msg: {
19746 const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} which has non-void fields", .{19766 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
19747 union_ty.fmt(target),19767 const msg = try sema.errMsg(
19748 });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 );
19749 errdefer msg.destroy(sema.gpa);19773 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 }
19750 try sema.addDeclaredHereNote(msg, union_ty);19783 try sema.addDeclaredHereNote(msg, union_ty);
19751 break :msg msg;19784 break :msg msg;
19752 };19785 };
...@@ -21835,7 +21868,7 @@ fn resolveTypeFieldsUnion(...@@ -21835,7 +21868,7 @@ fn resolveTypeFieldsUnion(
21835 }21868 }
2183621869
21837 union_obj.status = .field_types_wip;21870 union_obj.status = .field_types_wip;
21838 try semaUnionFields(sema.mod, union_obj);21871 try semaUnionFields(block, sema.mod, union_obj);
21839 union_obj.status = .have_field_types;21872 union_obj.status = .have_field_types;
21840}21873}
2184121874
...@@ -22044,7 +22077,21 @@ fn semaStructFields(...@@ -22044,7 +22077,21 @@ fn semaStructFields(
22044 }22077 }
2204522078
22046 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);22079 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 }
22048 gop.value_ptr.* = .{22095 gop.value_ptr.* = .{
22049 .ty = try field_ty.copy(decl_arena_allocator),22096 .ty = try field_ty.copy(decl_arena_allocator),
22050 .abi_align = 0,22097 .abi_align = 0,
...@@ -22075,7 +22122,7 @@ fn semaStructFields(...@@ -22075,7 +22122,7 @@ fn semaStructFields(
22075 }22122 }
22076}22123}
2207722124
22078fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {22125fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void {
22079 const tracy = trace(@src());22126 const tracy = trace(@src());
22080 defer tracy.end();22127 defer tracy.end();
2208122128
...@@ -22175,6 +22222,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -22175,6 +22222,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22175 var int_tag_ty: Type = undefined;22222 var int_tag_ty: Type = undefined;
22176 var enum_field_names: ?*Module.EnumNumbered.NameMap = null;22223 var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
22177 var enum_value_map: ?*Module.EnumNumbered.ValueMap = null;22224 var enum_value_map: ?*Module.EnumNumbered.ValueMap = null;
22225 var tag_ty_field_names: ?Module.EnumFull.NameMap = null;
22178 if (tag_type_ref != .none) {22226 if (tag_type_ref != .none) {
22179 const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref);22227 const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref);
22180 if (small.auto_enum_tag) {22228 if (small.auto_enum_tag) {
...@@ -22187,6 +22235,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -22187,6 +22235,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22187 } else {22235 } else {
22188 // The provided type is the enum tag type.22236 // The provided type is the enum tag type.
22189 union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator);22237 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);
22190 }22242 }
22191 } else {22243 } else {
22192 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis22244 // 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 {...@@ -22295,7 +22347,35 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22295 }22347 }
2229622348
22297 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);22349 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
22299 gop.value_ptr.* = .{22379 gop.value_ptr.* = .{
22300 .ty = try field_ty.copy(decl_arena_allocator),22380 .ty = try field_ty.copy(decl_arena_allocator),
22301 .abi_align = 0,22381 .abi_align = 0,
...@@ -22310,6 +22390,24 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -22310,6 +22390,24 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22310 gop.value_ptr.abi_align = 0;22390 gop.value_ptr.abi_align = 0;
22311 }22391 }
22312 }22392 }
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 }
22313}22411}
2231422412
22315fn generateUnionTagTypeNumbered(22413fn generateUnionTagTypeNumbered(
src/type.zig+44
...@@ -5312,6 +5312,50 @@ pub const Type = extern union {...@@ -5312,6 +5312,50 @@ pub const Type = extern union {
5312 }5312 }
5313 }5313 }
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
5315 /// Asserts the type is an enum.5359 /// Asserts the type is an enum.
5316 pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {5360 pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {
5317 const S = struct {5361 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