From 07f64a2e13ab80acffba7f7bdd5d7c58df7893c0 Mon Sep 17 00:00:00 2001 From: Martin Hafskjold Thoresen Date: Wed, 10 Aug 2022 23:32:02 +0200 Subject: [PATCH 1/3] Sema: error on ambiguous coercion of comptime float and ints The following, from the documentation as of the time of writing, illustrates the problem: ```zig // Compile time coercion of float to int test "implicit cast to comptime_int" { var f: f32 = 54.0 / 5; _ = f; } ``` It is not clear how to unify the types of 54.0 and 5 to perform the division. We can either - cast 54.0 to comptime_int resulting in @as(comptime_int, 10), which is casted to @as(f32, 10), or - cast 5 to comptime_float resulting in @as(comptime_float, 10.8), which is casted to @as(f32, 10.8) Since the two resulting values are different, a compiler error is appropriate. If we know that casting to either type will result in the same value we don't need to error. For instance, 10.0 / 2 is okay, as is 10 / 2.0. Fixes: #12364 --- src/Sema.zig | 16 ++++++++++++++++ test/behavior/floatop.zig | 8 ++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index f1d140520c..e5700a6fe4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11202,6 +11202,22 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs); + if ((lhs_ty.tag() == .comptime_float and rhs_ty.tag() == .comptime_int) or + (lhs_ty.tag() == .comptime_int and rhs_ty.tag() == .comptime_float)) + { + // If it makes a difference whether we coerce to ints or floats before doing the division, error. + // If lhs % rhs is 0, it doesn't matter. + var lhs_val = maybe_lhs_val orelse unreachable; + var rhs_val = maybe_rhs_val orelse unreachable; + var rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target) catch unreachable; + var float_rem = rem.toFloat(f32); + if (float_rem != 0.0) { + return sema.fail(block, src, "ambiguous coercion of division operands: '{s}' and '{s}': division has non-zero reminder: {d}", .{ + @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()), float_rem, + }); + } + } + // TODO: emit compile error when .div is used on integers and there would be an // ambiguous result between div_floor and div_trunc. diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index c057f7a842..a5eb25d4f5 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -194,8 +194,8 @@ fn testSin() !void { const eps = epsForType(ty); try expect(@sin(@as(ty, 0)) == 0); try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi)), 0, eps)); - try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 2)), 1, eps)); - try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 4)), 0.7071067811865475, eps)); + try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 2.0)), 1, eps)); + try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 4.0)), 0.7071067811865475, eps)); } { @@ -228,8 +228,8 @@ fn testCos() !void { const eps = epsForType(ty); try expect(@cos(@as(ty, 0)) == 1); try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi)), -1, eps)); - try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 2)), 0, eps)); - try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 4)), 0.7071067811865475, eps)); + try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 2.0)), 0, eps)); + try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 4.0)), 0.7071067811865475, eps)); } { From 59b6483d63ca68f63c0b3758e80da918629ada2c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 16 Aug 2022 16:17:56 +0300 Subject: [PATCH 2/3] add test --- src/Sema.zig | 17 +++++++------ ...mbiguous_coercion_of_division_operands.zig | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 test/cases/compile_errors/ambiguous_coercion_of_division_operands.zig diff --git a/src/Sema.zig b/src/Sema.zig index e5700a6fe4..29a53ebb2a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11202,18 +11202,17 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs); - if ((lhs_ty.tag() == .comptime_float and rhs_ty.tag() == .comptime_int) or - (lhs_ty.tag() == .comptime_int and rhs_ty.tag() == .comptime_float)) + if ((lhs_ty.zigTypeTag() == .ComptimeFloat and rhs_ty.zigTypeTag() == .ComptimeInt) or + (lhs_ty.zigTypeTag() == .ComptimeInt and rhs_ty.zigTypeTag() == .ComptimeFloat)) { // If it makes a difference whether we coerce to ints or floats before doing the division, error. // If lhs % rhs is 0, it doesn't matter. - var lhs_val = maybe_lhs_val orelse unreachable; - var rhs_val = maybe_rhs_val orelse unreachable; - var rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target) catch unreachable; - var float_rem = rem.toFloat(f32); - if (float_rem != 0.0) { - return sema.fail(block, src, "ambiguous coercion of division operands: '{s}' and '{s}': division has non-zero reminder: {d}", .{ - @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()), float_rem, + const lhs_val = maybe_lhs_val orelse unreachable; + const rhs_val = maybe_rhs_val orelse unreachable; + const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target) catch unreachable; + if (rem.compareWithZero(.neq)) { + return sema.fail(block, src, "ambiguous coercion of division operands '{s}' and '{s}'; division has non-zero reminder '{}'", .{ + @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()), rem.fmtValue(resolved_type, sema.mod), }); } } diff --git a/test/cases/compile_errors/ambiguous_coercion_of_division_operands.zig b/test/cases/compile_errors/ambiguous_coercion_of_division_operands.zig new file mode 100644 index 0000000000..d8fa4e5726 --- /dev/null +++ b/test/cases/compile_errors/ambiguous_coercion_of_division_operands.zig @@ -0,0 +1,24 @@ +export fn entry1() void { + var f: f32 = 54.0 / 5; + _ = f; +} +export fn entry2() void { + var f: f32 = 54 / 5.0; + _ = f; +} +export fn entry3() void { + var f: f32 = 55.0 / 5; + _ = f; +} +export fn entry4() void { + var f: f32 = 55 / 5.0; + _ = f; +} + + +// error +// backend=stage2 +// target=native +// +// :2:23: error: ambiguous coercion of division operands 'comptime_float' and 'comptime_int'; division has non-zero reminder '4' +// :6:21: error: ambiguous coercion of division operands 'comptime_int' and 'comptime_float'; division has non-zero reminder '4' From 2cccd144914d8715894458f317fa4c3c572cdcad Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 17 Aug 2022 14:06:47 -0700 Subject: [PATCH 3/3] fix typo in compile error message --- src/Sema.zig | 2 +- .../ambiguous_coercion_of_division_operands.zig | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 29a53ebb2a..5fd1c1ce4c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11211,7 +11211,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const rhs_val = maybe_rhs_val orelse unreachable; const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target) catch unreachable; if (rem.compareWithZero(.neq)) { - return sema.fail(block, src, "ambiguous coercion of division operands '{s}' and '{s}'; division has non-zero reminder '{}'", .{ + return sema.fail(block, src, "ambiguous coercion of division operands '{s}' and '{s}'; non-zero remainder '{}'", .{ @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()), rem.fmtValue(resolved_type, sema.mod), }); } diff --git a/test/cases/compile_errors/ambiguous_coercion_of_division_operands.zig b/test/cases/compile_errors/ambiguous_coercion_of_division_operands.zig index d8fa4e5726..f3e51a1bed 100644 --- a/test/cases/compile_errors/ambiguous_coercion_of_division_operands.zig +++ b/test/cases/compile_errors/ambiguous_coercion_of_division_operands.zig @@ -15,10 +15,9 @@ export fn entry4() void { _ = f; } - // error // backend=stage2 // target=native // -// :2:23: error: ambiguous coercion of division operands 'comptime_float' and 'comptime_int'; division has non-zero reminder '4' -// :6:21: error: ambiguous coercion of division operands 'comptime_int' and 'comptime_float'; division has non-zero reminder '4' +// :2:23: error: ambiguous coercion of division operands 'comptime_float' and 'comptime_int'; non-zero remainder '4' +// :6:21: error: ambiguous coercion of division operands 'comptime_int' and 'comptime_float'; non-zero remainder '4'