| ... | @@ -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 | } |
| 1591 | | 1591 | |
| | 1592 | fn 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 | |
| 1592 | fn errMsg( | 1607 | fn 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 active | 17591 | 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 | } |
| 19742 | | 19764 | |
| 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 | } |
| 21836 | | 21869 | |
| 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 | } |
| 21841 | | 21874 | |
| ... | @@ -22044,7 +22077,21 @@ fn semaStructFields( | ... | @@ -22044,7 +22077,21 @@ fn semaStructFields( |
| 22044 | } | 22077 | } |
| 22045 | | 22078 | |
| 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 | } |
| 22077 | | 22124 | |
| 22078 | fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { | 22125 | fn 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(); |
| 22081 | | 22128 | |
| ... | @@ -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 analysis | 22244 | // 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 | } |
| 22296 | | 22348 | |
| 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 | } |
| 22314 | | 22412 | |
| 22315 | fn generateUnionTagTypeNumbered( | 22413 | fn generateUnionTagTypeNumbered( |