diff --git a/src/Sema.zig b/src/Sema.zig index 6f1259ed82..bb14943e22 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19853,6 +19853,26 @@ fn coerce( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, true) catch |err| switch (err) { + error.NotCoercible => unreachable, + else => |e| return e, + }; +} + +const CoersionError = CompileError || error{ + /// When coerce is called recursively, this error should be returned instead of using `fail` + /// to ensure correct types in compile errors. + NotCoercible, +}; + +fn coerceExtra( + sema: *Sema, + block: *Block, + dest_ty_unresolved: Type, + inst: Air.Inst.Ref, + inst_src: LazySrcLoc, + report_err: bool, +) CoersionError!Air.Inst.Ref { switch (dest_ty_unresolved.tag()) { .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src), .generic_poison => return inst, @@ -19869,7 +19889,7 @@ fn coerce( const arena = sema.arena; const maybe_inst_val = try sema.resolveMaybeUndefVal(block, inst_src, inst); - const 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); if (in_memory_result == .ok) { if (maybe_inst_val) |val| { // Keep the comptime Value representation; take the new type. @@ -19882,7 +19902,7 @@ fn coerce( const is_undef = if (maybe_inst_val) |val| val.isUndef() else false; switch (dest_ty.zigTypeTag()) { - .Optional => { + .Optional => optional: { // undefined sets the optional bit also to undefined. if (is_undef) { return sema.addConstUndef(dest_ty); @@ -19903,10 +19923,19 @@ fn coerce( // T to ?T const child_type = try dest_ty.optionalChildAlloc(sema.arena); - const intermediate = try sema.coerce(block, child_type, inst, inst_src); - return sema.wrapOptional(block, dest_ty, intermediate, inst_src); + const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, false) catch |err| switch (err) { + 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); + } + break :optional; + }, + else => |e| return e, + }; + return try sema.wrapOptional(block, dest_ty, intermediate, inst_src); }, - .Pointer => { + .Pointer => pointer: { const dest_info = dest_ty.ptrInfo().data; // Function body to function pointer. @@ -20011,16 +20040,26 @@ fn coerce( return sema.addConstant(dest_ty, Value.@"null"); }, .ComptimeInt => { - const addr = try sema.coerce(block, Type.usize, inst, inst_src); - return sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); + const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, false) catch |err| switch (err) { + error.NotCoercible => break :pointer, + else => |e| return e, + }; + return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); }, .Int => { const ptr_size_ty = switch (inst_ty.intInfo(target).signedness) { .signed => Type.isize, .unsigned => Type.usize, }; - const addr = try sema.coerce(block, ptr_size_ty, inst, inst_src); - return sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); + const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, 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); + break :pointer; + }, + else => |e| return e, + }; + return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); }, .Pointer => p: { const inst_info = inst_ty.ptrInfo().data; @@ -20155,6 +20194,7 @@ fn coerce( if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { // comptime known integer to other number if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) { + if (!report_err) return error.NotCoercible; return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); } return try sema.addConstant(dest_ty, val); @@ -20356,6 +20396,8 @@ fn coerce( return sema.addConstUndef(dest_ty); } + if (!report_err) return error.NotCoercible; + const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) }); errdefer msg.destroy(sema.gpa); @@ -20534,8 +20576,10 @@ const InMemoryCoercionResult = union(enum) { cur = pair.child; }, .optional_shape => |pair| { - try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type '{}'", .{ - pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod), + var buf_actual: Type.Payload.ElemType = undefined; + var buf_wanted: Type.Payload.ElemType = undefined; + try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ + pair.actual.optionalChild(&buf_actual).fmt(sema.mod), pair.wanted.optionalChild(&buf_wanted).fmt(sema.mod), }); break; }, diff --git a/test/cases/compile_errors/any_typed_null_to_any_typed_optional.zig b/test/cases/compile_errors/any_typed_null_to_any_typed_optional.zig index ad6427d224..6b3ffaca2f 100644 --- a/test/cases/compile_errors/any_typed_null_to_any_typed_optional.zig +++ b/test/cases/compile_errors/any_typed_null_to_any_typed_optional.zig @@ -1,11 +1,11 @@ -pub fn main() void { +pub export fn entry() void { var a: ?*anyopaque = undefined; a = @as(?usize, null); } // error -// output_mode=Exe -// backend=stage2,llvm -// target=x86_64-linux,x86_64-macos +// backend=stage2 +// target=native // -// :3:21: error: expected type '*anyopaque', found '?usize' +// :3:21: error: expected type '?*anyopaque', found '?usize' +// :3:21: note: optional type child 'usize' cannot cast into optional type child '*anyopaque' diff --git a/test/cases/compile_errors/cast_between_optional_T_where_T_is_not_a_pointer.zig b/test/cases/compile_errors/cast_between_optional_T_where_T_is_not_a_pointer.zig new file mode 100644 index 0000000000..a09059eb3e --- /dev/null +++ b/test/cases/compile_errors/cast_between_optional_T_where_T_is_not_a_pointer.zig @@ -0,0 +1,16 @@ +pub const fnty1 = ?*const fn (i8) void; +pub const fnty2 = ?*const fn (u64) void; +export fn entry() void { + var a: fnty1 = undefined; + var b: fnty2 = undefined; + a = b; +} + +// error +// backend=stage2 +// target=native +// +// :6:9: error: expected type '?*const fn(i8) void', found '?*const fn(u64) void' +// :6:9: note: pointer type child 'fn(u64) void' cannot cast into pointer type child 'fn(i8) void' +// :6:9: note: parameter 0 'u64' cannot cast into 'i8' +// :6:9: note: unsigned 64-bit int cannot represent all possible signed 8-bit values diff --git a/test/cases/compile_errors/implicit_cast_to_c_ptr_from_int.zig b/test/cases/compile_errors/implicit_cast_to_c_ptr_from_int.zig new file mode 100644 index 0000000000..bcf15569ff --- /dev/null +++ b/test/cases/compile_errors/implicit_cast_to_c_ptr_from_int.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +export fn entry1() void { + _ = @as([*c]u8, @as(u65, std.math.maxInt(u65))); +} +export fn entry2() void { + _ = @as([*c]u8, std.math.maxInt(u65)); +} + +// error +// backend=stage2 +// target=native +// +// :3:21: error: expected type '[*c]u8', found 'u65' +// :3:21: note: unsigned 64-bit int cannot represent all possible unsigned 65-bit values +// :6:36: error: expected type '[*c]u8', found 'comptime_int' diff --git a/test/cases/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig b/test/cases/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig deleted file mode 100644 index b8392ff5db..0000000000 --- a/test/cases/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig +++ /dev/null @@ -1,15 +0,0 @@ -pub const fnty1 = ?fn (i8) void; -pub const fnty2 = ?fn (u64) void; -export fn entry() void { - var a: fnty1 = undefined; - var b: fnty2 = undefined; - a = b; -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:6:9: error: expected type '?fn(i8) void', found '?fn(u64) void' -// tmp.zig:6:9: note: optional type child 'fn(u64) void' cannot cast into optional type child 'fn(i8) void' diff --git a/test/cases/compile_errors/type_mismatch_in_C_prototype_with_varargs.zig b/test/cases/compile_errors/type_mismatch_in_C_prototype_with_varargs.zig index 1e7acb55b8..66256fc9e9 100644 --- a/test/cases/compile_errors/type_mismatch_in_C_prototype_with_varargs.zig +++ b/test/cases/compile_errors/type_mismatch_in_C_prototype_with_varargs.zig @@ -10,6 +10,6 @@ export fn main() void { // backend=stage2 // target=native // -// :5:22: error: expected type 'fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void' +// :5:22: error: expected type '?fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void' // :5:22: note: parameter 0 '[*:0]u8' cannot cast into '[*c]u8' // :5:22: note: '[*c]u8' could have null values which are illegal in type '[*:0]u8'