authorgravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2022-03-19 19:32:31+01:00
committergravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2022-04-03 13:49:34+02:00
logb922caf1691111e5c23f01afbe1f1c9b5104807b
treeb4fa0d8fcab350779c546041f6b694e4ac47ef54
parentf7f4702795d76c606d119941e2cf5d5c3e2045b6

sema: add compile error for missing/extra enum fields in union decl


2 files changed, 86 insertions(+), 2 deletions(-)

src/Sema.zig+42-2
...@@ -21727,7 +21727,7 @@ fn resolveTypeFieldsUnion(...@@ -21727,7 +21727,7 @@ fn resolveTypeFieldsUnion(
21727 }21727 }
2172821728
21729 union_obj.status = .field_types_wip;21729 union_obj.status = .field_types_wip;
21730 try semaUnionFields(sema.mod, union_obj);21730 try semaUnionFields(block, sema.mod, union_obj);
21731 union_obj.status = .have_field_types;21731 union_obj.status = .have_field_types;
21732}21732}
2173321733
...@@ -21967,7 +21967,7 @@ fn semaStructFields(...@@ -21967,7 +21967,7 @@ fn semaStructFields(
21967 }21967 }
21968}21968}
2196921969
21970fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {21970fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void {
21971 const tracy = trace(@src());21971 const tracy = trace(@src());
21972 defer tracy.end();21972 defer tracy.end();
2197321973
...@@ -22067,6 +22067,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -22067,6 +22067,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22067 var int_tag_ty: Type = undefined;22067 var int_tag_ty: Type = undefined;
22068 var enum_field_names: ?*Module.EnumNumbered.NameMap = null;22068 var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
22069 var enum_value_map: ?*Module.EnumNumbered.ValueMap = null;22069 var enum_value_map: ?*Module.EnumNumbered.ValueMap = null;
22070 var tag_ty_field_names: ?Module.EnumFull.NameMap = null;
22070 if (tag_type_ref != .none) {22071 if (tag_type_ref != .none) {
22071 const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref);22072 const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref);
22072 if (small.auto_enum_tag) {22073 if (small.auto_enum_tag) {
...@@ -22079,6 +22080,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -22079,6 +22080,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22079 } else {22080 } else {
22080 // The provided type is the enum tag type.22081 // The provided type is the enum tag type.
22081 union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator);22082 union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator);
22083 // The fields of the union must match the enum exactly.
22084 // Store a copy of the enum field names so we can check for
22085 // missing or extraneous fields later.
22086 tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(decl_arena_allocator);
22082 }22087 }
22083 } else {22088 } else {
22084 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis22089 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
...@@ -22172,6 +22177,20 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -22172,6 +22177,20 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22172 set.putAssumeCapacity(field_name, {});22177 set.putAssumeCapacity(field_name, {});
22173 }22178 }
2217422179
22180 if (tag_ty_field_names) |*names| {
22181 const enum_has_field = names.contains(field_name);
22182 if (!enum_has_field) {
22183 const msg = msg: {
22184 const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name });
22185 errdefer msg.destroy(sema.gpa);
22186 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
22187 break :msg msg;
22188 };
22189 return sema.failWithOwnedErrorMsg(block, msg);
22190 }
22191 _ = names.orderedRemove(field_name);
22192 }
22193
22175 const field_ty: Type = if (!has_type)22194 const field_ty: Type = if (!has_type)
22176 Type.void22195 Type.void
22177 else if (field_type_ref == .none)22196 else if (field_type_ref == .none)
...@@ -22202,6 +22221,27 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -22202,6 +22221,27 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
22202 gop.value_ptr.abi_align = 0;22221 gop.value_ptr.abi_align = 0;
22203 }22222 }
22204 }22223 }
22224
22225 if (tag_ty_field_names) |names| {
22226 if (names.count() > 0) {
22227 const msg = msg: {
22228 const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
22229 errdefer msg.destroy(sema.gpa);
22230
22231 const enum_ty = union_obj.tag_ty;
22232 const tree = try sema.getAstTree(block);
22233 const enum_decl = enum_ty.getOwnerDecl();
22234 for (names.keys()) |field_name| {
22235 const field_index = enum_ty.enumFieldIndex(field_name).?;
22236 const field_src = enumFieldSrcLoc(enum_decl, tree.*, enum_ty.getNodeOffset(), field_index);
22237 try sema.mod.errNoteNonLazy(field_src.toSrcLoc(enum_decl), msg, "field '{s}' missing, declared here", .{field_name});
22238 }
22239 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
22240 break :msg msg;
22241 };
22242 return sema.failWithOwnedErrorMsg(block, msg);
22243 }
22244 }
22205}22245}
2220622246
22207fn generateUnionTagTypeNumbered(22247fn generateUnionTagTypeNumbered(
src/type.zig+44
...@@ -5305,6 +5305,50 @@ pub const Type = extern union {...@@ -5305,6 +5305,50 @@ pub const Type = extern union {
5305 }5305 }
5306 }5306 }
53075307
5308 pub fn getNodeOffset(ty: Type) i32 {
5309 switch (ty.tag()) {
5310 .enum_full, .enum_nonexhaustive => {
5311 const enum_full = ty.cast(Payload.EnumFull).?.data;
5312 return enum_full.node_offset;
5313 },
5314 .enum_numbered => return ty.castTag(.enum_numbered).?.data.node_offset,
5315 .enum_simple => {
5316 const enum_simple = ty.castTag(.enum_simple).?.data;
5317 return enum_simple.node_offset;
5318 },
5319 .@"struct" => {
5320 const struct_obj = ty.castTag(.@"struct").?.data;
5321 return struct_obj.node_offset;
5322 },
5323 .error_set => {
5324 const error_set = ty.castTag(.error_set).?.data;
5325 return error_set.node_offset;
5326 },
5327 .@"union", .union_tagged => {
5328 const union_obj = ty.cast(Payload.Union).?.data;
5329 return union_obj.node_offset;
5330 },
5331 .@"opaque" => {
5332 const opaque_obj = ty.cast(Payload.Opaque).?.data;
5333 return opaque_obj.node_offset;
5334 },
5335 .atomic_order,
5336 .atomic_rmw_op,
5337 .calling_convention,
5338 .address_space,
5339 .float_mode,
5340 .reduce_op,
5341 .call_options,
5342 .prefetch_options,
5343 .export_options,
5344 .extern_options,
5345 .type_info,
5346 => unreachable, // These need to be resolved earlier.
5347
5348 else => unreachable,
5349 }
5350 }
5351
5308 /// Asserts the type is an enum.5352 /// Asserts the type is an enum.
5309 pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {5353 pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {
5310 const S = struct {5354 const S = struct {