Sema: add error note for failed coercions to optional types and error unions

This commit is contained in:
Will Lillis 2024-07-16 12:42:13 -04:00 committed by GitHub
parent 9356cb1475
commit a9d544575d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 87 additions and 29 deletions

View File

@ -11312,7 +11312,7 @@ const SwitchProngAnalysis = struct {
const first_non_imc = in_mem: {
for (field_indices, 0..) |field_idx, i| {
const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded)) {
if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
break :in_mem i;
}
}
@ -11335,7 +11335,7 @@ const SwitchProngAnalysis = struct {
const next = first_non_imc + 1;
for (field_indices[next..], next..) |field_idx, i| {
const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded)) {
if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
in_mem_coercible.unset(i);
}
}
@ -23162,6 +23162,7 @@ fn ptrCastFull(
mod.getTarget(),
src,
operand_src,
null,
);
if (imc_res == .ok) break :check_child;
return sema.failWithOwnedErrorMsg(block, msg: {
@ -25772,7 +25773,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const dest_elem_ty = dest_ty.elemType2(mod);
const src_elem_ty = src_ty.elemType2(mod);
if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) {
if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src, null)) {
return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{});
}
@ -29188,7 +29189,7 @@ fn coerceExtra(
const maybe_inst_val = try sema.resolveValue(inst);
var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
if (in_memory_result == .ok) {
if (maybe_inst_val) |val| {
return sema.coerceInMemory(val, dest_ty);
@ -29243,7 +29244,7 @@ fn coerceExtra(
error.NotCoercible => {
if (in_memory_result == .no_match) {
// Try to give more useful notes
in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src);
in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
}
break :optional;
},
@ -29273,7 +29274,7 @@ fn coerceExtra(
const array_elem_ty = array_ty.childType(zcu);
if (array_ty.arrayLen(zcu) != 1) break :single_item;
const dest_is_mut = !dest_info.flags.is_const;
switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) {
.ok => {},
else => break :single_item,
}
@ -29290,7 +29291,7 @@ fn coerceExtra(
const dest_is_mut = !dest_info.flags.is_const;
const dst_elem_type = Type.fromInterned(dest_info.child);
const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src);
const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val);
switch (elem_res) {
.ok => {},
else => {
@ -29351,7 +29352,7 @@ fn coerceExtra(
const src_elem_ty = inst_ty.childType(zcu);
const dest_is_mut = !dest_info.flags.is_const;
const dst_elem_type = Type.fromInterned(dest_info.child);
switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) {
.ok => {},
else => break :src_c_ptr,
}
@ -29404,7 +29405,7 @@ fn coerceExtra(
const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
error.NotCoercible => {
// Try to give more useful notes
in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
break :pointer;
},
else => |e| return e,
@ -29422,6 +29423,7 @@ fn coerceExtra(
target,
dest_ty_src,
inst_src,
maybe_inst_val,
)) {
.ok => {},
else => break :p,
@ -29526,6 +29528,7 @@ fn coerceExtra(
target,
dest_ty_src,
inst_src,
maybe_inst_val,
)) {
.ok => {},
else => break :p,
@ -29705,7 +29708,14 @@ fn coerceExtra(
else => eu: {
// T to E!T
return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) {
error.NotCoercible => break :eu,
error.NotCoercible => {
if (in_memory_result == .no_match) {
const payload_type = dest_ty.errorUnionPayload(zcu);
// Try to give more useful notes
in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
}
break :eu;
},
else => |e| return e,
};
},
@ -29730,6 +29740,7 @@ fn coerceExtra(
target,
dest_ty_src,
inst_src,
maybe_inst_val,
)) {
break :array_to_array;
}
@ -29805,7 +29816,7 @@ fn coerceExtra(
// E!T to T
if (inst_ty.zigTypeTag(zcu) == .ErrorUnion and
(try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
(try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok)
{
try sema.errNote(inst_src, msg, "cannot convert error union to payload type", .{});
try sema.errNote(inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
@ -29813,7 +29824,7 @@ fn coerceExtra(
// ?T to T
if (inst_ty.zigTypeTag(zcu) == .Optional and
(try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(zcu), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
(try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok)
{
try sema.errNote(inst_src, msg, "cannot convert optional to payload type", .{});
try sema.errNote(inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
@ -29859,6 +29870,7 @@ const InMemoryCoercionResult = union(enum) {
ok,
no_match: Pair,
int_not_coercible: Int,
comptime_int_not_coercible: TypeValuePair,
error_union_payload: PairAndChild,
array_len: IntPair,
array_sentinel: Sentinel,
@ -29895,6 +29907,11 @@ const InMemoryCoercionResult = union(enum) {
wanted: Type,
};
const TypeValuePair = struct {
actual: Value,
wanted: Type,
};
const PairAndChild = struct {
child: *InMemoryCoercionResult,
actual: Type,
@ -29988,6 +30005,12 @@ const InMemoryCoercionResult = union(enum) {
});
break;
},
.comptime_int_not_coercible => |int| {
try sema.errNote(src, msg, "type '{}' cannot represent value '{}'", .{
int.wanted.fmt(pt), int.actual.fmtValue(pt, sema),
});
break;
},
.error_union_payload => |pair| {
try sema.errNote(src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{
pair.actual.fmt(pt), pair.wanted.fmt(pt),
@ -30229,6 +30252,7 @@ pub fn coerceInMemoryAllowed(
target: std.Target,
dest_src: LazySrcLoc,
src_src: LazySrcLoc,
src_val: ?Value,
) CompileError!InMemoryCoercionResult {
const pt = sema.pt;
const mod = pt.zcu;
@ -30264,6 +30288,15 @@ pub fn coerceInMemoryAllowed(
}
}
// Comptime int to regular int.
if (dest_tag == .Int and src_tag == .ComptimeInt) {
if (src_val) |val| {
if (!(try sema.intFitsInType(val, dest_ty, null))) {
return .{ .comptime_int_not_coercible = .{ .wanted = dest_ty, .actual = val } };
}
}
}
// Differently-named floats with the same number of bits.
if (dest_tag == .Float and src_tag == .Float) {
const dest_bits = dest_ty.floatBits(target);
@ -30296,7 +30329,7 @@ pub fn coerceInMemoryAllowed(
if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) {
const dest_payload = dest_ty.errorUnionPayload(mod);
const src_payload = src_ty.errorUnionPayload(mod);
const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src);
const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src, null);
if (child != .ok) {
return InMemoryCoercionResult{ .error_union_payload = .{
.child = try child.dupe(sema.arena),
@ -30304,7 +30337,7 @@ pub fn coerceInMemoryAllowed(
.wanted = dest_payload,
} };
}
return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src);
return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src, null);
}
// Error Sets
@ -30323,7 +30356,7 @@ pub fn coerceInMemoryAllowed(
} };
}
const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src);
const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src, null);
if (child != .ok) {
return InMemoryCoercionResult{ .array_elem = .{
.child = try child.dupe(sema.arena),
@ -30362,7 +30395,7 @@ pub fn coerceInMemoryAllowed(
const dest_elem_ty = dest_ty.scalarType(mod);
const src_elem_ty = src_ty.scalarType(mod);
const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null);
if (child != .ok) {
return InMemoryCoercionResult{ .vector_elem = .{
.child = try child.dupe(sema.arena),
@ -30389,7 +30422,7 @@ pub fn coerceInMemoryAllowed(
const dest_elem_ty = dest_ty.childType(mod);
const src_elem_ty = src_ty.childType(mod);
const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null);
if (child != .ok) {
return InMemoryCoercionResult{ .array_elem = .{
.child = try child.dupe(sema.arena),
@ -30429,7 +30462,7 @@ pub fn coerceInMemoryAllowed(
const dest_child_type = dest_ty.optionalChild(mod);
const src_child_type = src_ty.optionalChild(mod);
const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src);
const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src, null);
if (child != .ok) {
return InMemoryCoercionResult{ .optional_child = .{
.child = try child.dupe(sema.arena),
@ -30451,7 +30484,7 @@ pub fn coerceInMemoryAllowed(
if (dest_ty.structFieldAlign(field_idx, pt) != src_ty.structFieldAlign(field_idx, pt)) break :tuple;
const dest_field_ty = dest_ty.structFieldType(field_idx, mod);
const src_field_ty = src_ty.structFieldType(field_idx, mod);
const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src);
const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null);
if (field != .ok) break :tuple;
}
return .ok;
@ -30598,7 +30631,7 @@ fn coerceInMemoryAllowedFns(
else => {
const dest_return_type = Type.fromInterned(dest_info.return_type);
const src_return_type = Type.fromInterned(src_info.return_type);
const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src);
const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src, null);
if (rt != .ok) {
return InMemoryCoercionResult{ .fn_return_type = .{
.child = try rt.dupe(sema.arena),
@ -30644,7 +30677,7 @@ fn coerceInMemoryAllowedFns(
.generic_poison_type => {},
else => {
// Note: Cast direction is reversed here.
const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src);
const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src, null);
if (param != .ok) {
return InMemoryCoercionResult{ .fn_param = .{
.child = try param.dupe(sema.arena),
@ -30708,13 +30741,13 @@ fn coerceInMemoryAllowedPtrs(
const dest_child = Type.fromInterned(dest_info.child);
const src_child = Type.fromInterned(src_info.child);
const child = try sema.coerceInMemoryAllowed(block, dest_child, src_child, !dest_info.flags.is_const, target, dest_src, src_src);
const child = try sema.coerceInMemoryAllowed(block, dest_child, src_child, !dest_info.flags.is_const, target, dest_src, src_src, null);
if (child != .ok) allow: {
// As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice.
// `*[n:s]T` cannot coerce in memory to `*[n]T` since they have different sizes.
if (src_child.zigTypeTag(zcu) == .Array and dest_child.zigTypeTag(zcu) == .Array and
src_child.sentinel(zcu) != null and dest_child.sentinel(zcu) == null and
.ok == try sema.coerceInMemoryAllowed(block, dest_child.childType(zcu), src_child.childType(zcu), !dest_info.flags.is_const, target, dest_src, src_src))
.ok == try sema.coerceInMemoryAllowed(block, dest_child.childType(zcu), src_child.childType(zcu), !dest_info.flags.is_const, target, dest_src, src_src, null))
{
break :allow;
}
@ -31593,7 +31626,7 @@ fn coerceArrayLike(
const target = mod.getTarget();
// try coercion of the whole array
const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, null);
if (in_memory_result == .ok) {
if (try sema.resolveValue(inst)) |inst_val| {
// These types share the same comptime value representation.
@ -34108,13 +34141,13 @@ fn resolvePeerTypesInner(
const peer_elem_ty = ty.childType(mod);
if (!peer_elem_ty.eql(elem_ty, mod)) coerce: {
const peer_elem_coerces_to_elem =
try sema.coerceInMemoryAllowed(block, elem_ty, peer_elem_ty, false, mod.getTarget(), src, src);
try sema.coerceInMemoryAllowed(block, elem_ty, peer_elem_ty, false, mod.getTarget(), src, src, null);
if (peer_elem_coerces_to_elem == .ok) {
break :coerce;
}
const elem_coerces_to_peer_elem =
try sema.coerceInMemoryAllowed(block, peer_elem_ty, elem_ty, false, mod.getTarget(), src, src);
try sema.coerceInMemoryAllowed(block, peer_elem_ty, elem_ty, false, mod.getTarget(), src, src, null);
if (elem_coerces_to_peer_elem == .ok) {
elem_ty = peer_elem_ty;
break :coerce;
@ -35039,12 +35072,12 @@ fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_
const target = sema.pt.zcu.getTarget();
// ty_b -> ty_a
if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, target, src, src)) {
if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, target, src, src, null)) {
return ty_a;
}
// ty_a -> ty_b
if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, target, src, src)) {
if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, target, src, src, null)) {
return ty_b;
}

View File

@ -321,6 +321,7 @@ fn loadComptimePtrInner(
zcu.getTarget(),
src,
src,
null,
)) {
// We already have a value which is IMC to the desired type.
return .{ .success = base_val };
@ -353,6 +354,7 @@ fn loadComptimePtrInner(
zcu.getTarget(),
src,
src,
null,
)) {
// Changing the length of an array.
const skip_base: u64 = extra_base_index + if (load_ty.zigTypeTag(zcu) == .Array) skip: {
@ -721,6 +723,7 @@ fn prepareComptimePtrStore(
zcu.getTarget(),
src,
src,
null,
)) {
// The base strategy already gets us a value which the desired type is IMC to.
return base_strat;
@ -753,7 +756,7 @@ fn prepareComptimePtrStore(
else => unreachable,
};
const val_one_ty, const val_count = base_val.typeOf(zcu).arrayBase(zcu);
if (.ok != try sema.coerceInMemoryAllowed(block, val_one_ty, store_one_ty, true, zcu.getTarget(), src, src)) {
if (.ok != try sema.coerceInMemoryAllowed(block, val_one_ty, store_one_ty, true, zcu.getTarget(), src, src, null)) {
break :restructure_array;
}
if (base_elem_offset + extra_base_index + store_count > val_count) return .{ .out_of_bounds = oob_ty };

View File

@ -0,0 +1,11 @@
export fn b() void {
const x: anyerror!u8 = 256;
_ = x;
}
// error
// backend=stage2
// target=native
//
// 2:28: error: expected type 'anyerror!u8', found 'comptime_int'
// 2:28: note: type 'u8' cannot represent value '256'

View File

@ -0,0 +1,11 @@
export fn a() void {
const x: ?u8 = 256;
_ = x;
}
// error
// backend=stage2
// target=native
//
// 2:20: error: expected type '?u8', found 'comptime_int'
// 2:20: note: type 'u8' cannot represent value '256'