| ... | ... | @@ -1589,6 +1589,21 @@ fn errNote( |
| 1589 | 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 | 1607 | fn errMsg( |
| 1593 | 1608 | sema: *Sema, |
| 1594 | 1609 | block: *Block, |
| ... | ... | @@ -17573,9 +17588,15 @@ fn unionFieldVal( |
| 17573 | 17588 | if (tag_matches) { |
| 17574 | 17589 | return sema.addConstant(field.ty, tag_and_val.val); |
| 17575 | 17590 | } 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); |
| 17579 | 17600 | } |
| 17580 | 17601 | }, |
| 17581 | 17602 | .Packed, .Extern => { |
| ... | ... | @@ -19702,13 +19723,14 @@ fn coerceEnumToUnion( |
| 19702 | 19723 | const field = union_obj.fields.values()[field_index]; |
| 19703 | 19724 | const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty); |
| 19704 | 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 | 19726 | 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, |
| 19710 | 19730 | }); |
| 19711 | 19731 | errdefer msg.destroy(sema.gpa); |
| 19732 | |
| 19733 | try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name}); |
| 19712 | 19734 | try sema.addDeclaredHereNote(msg, union_ty); |
| 19713 | 19735 | break :msg msg; |
| 19714 | 19736 | }; |
| ... | ... | @@ -19740,13 +19762,24 @@ fn coerceEnumToUnion( |
| 19740 | 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 | 19765 | 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 | ); |
| 19749 | 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 | 19783 | try sema.addDeclaredHereNote(msg, union_ty); |
| 19751 | 19784 | break :msg msg; |
| 19752 | 19785 | }; |
| ... | ... | @@ -21835,7 +21868,7 @@ fn resolveTypeFieldsUnion( |
| 21835 | 21868 | } |
| 21836 | 21869 | |
| 21837 | 21870 | union_obj.status = .field_types_wip; |
| 21838 | | try semaUnionFields(sema.mod, union_obj); |
| 21871 | try semaUnionFields(block, sema.mod, union_obj); |
| 21839 | 21872 | union_obj.status = .have_field_types; |
| 21840 | 21873 | } |
| 21841 | 21874 | |
| ... | ... | @@ -22044,7 +22077,21 @@ fn semaStructFields( |
| 22044 | 22077 | } |
| 22045 | 22078 | |
| 22046 | 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 | 22095 | gop.value_ptr.* = .{ |
| 22049 | 22096 | .ty = try field_ty.copy(decl_arena_allocator), |
| 22050 | 22097 | .abi_align = 0, |
| ... | ... | @@ -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 | 22126 | const tracy = trace(@src()); |
| 22080 | 22127 | defer tracy.end(); |
| 22081 | 22128 | |
| ... | ... | @@ -22175,6 +22222,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { |
| 22175 | 22222 | var int_tag_ty: Type = undefined; |
| 22176 | 22223 | var enum_field_names: ?*Module.EnumNumbered.NameMap = null; |
| 22177 | 22224 | var enum_value_map: ?*Module.EnumNumbered.ValueMap = null; |
| 22225 | var tag_ty_field_names: ?Module.EnumFull.NameMap = null; |
| 22178 | 22226 | if (tag_type_ref != .none) { |
| 22179 | 22227 | const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref); |
| 22180 | 22228 | if (small.auto_enum_tag) { |
| ... | ... | @@ -22187,6 +22235,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { |
| 22187 | 22235 | } else { |
| 22188 | 22236 | // The provided type is the enum tag type. |
| 22189 | 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 | 22243 | } else { |
| 22192 | 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 | 22347 | } |
| 22296 | 22348 | |
| 22297 | 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 | 22379 | gop.value_ptr.* = .{ |
| 22300 | 22380 | .ty = try field_ty.copy(decl_arena_allocator), |
| 22301 | 22381 | .abi_align = 0, |
| ... | ... | @@ -22310,6 +22390,24 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { |
| 22310 | 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 | 22413 | fn generateUnionTagTypeNumbered( |