authorgravatar for git@mht.wtfMartin Hafskjold Thoresen <git@mht.wtf> 2022-08-10 23:32:02+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-17 14:09:09-07:00
log07f64a2e13ab80acffba7f7bdd5d7c58df7893c0
treee35bf8eea3846afca0a3d42f1436b1759f39c60a
parent070282a96ec23fb41041843d5753608ec5090f8b

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

2 files changed, 20 insertions(+), 4 deletions(-)

src/Sema.zig+16
......@@ -11202,6 +11202,22 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1120211202 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
1120311203 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
1120411204
11205 if ((lhs_ty.tag() == .comptime_float and rhs_ty.tag() == .comptime_int) or
11206 (lhs_ty.tag() == .comptime_int and rhs_ty.tag() == .comptime_float))
11207 {
11208 // If it makes a difference whether we coerce to ints or floats before doing the division, error.
11209 // If lhs % rhs is 0, it doesn't matter.
11210 var lhs_val = maybe_lhs_val orelse unreachable;
11211 var rhs_val = maybe_rhs_val orelse unreachable;
11212 var rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target) catch unreachable;
11213 var float_rem = rem.toFloat(f32);
11214 if (float_rem != 0.0) {
11215 return sema.fail(block, src, "ambiguous coercion of division operands: '{s}' and '{s}': division has non-zero reminder: {d}", .{
11216 @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()), float_rem,
11217 });
11218 }
11219 }
11220
1120511221 // TODO: emit compile error when .div is used on integers and there would be an
1120611222 // ambiguous result between div_floor and div_trunc.
1120711223
test/behavior/floatop.zig+4-4
......@@ -194,8 +194,8 @@ fn testSin() !void {
194194 const eps = epsForType(ty);
195195 try expect(@sin(@as(ty, 0)) == 0);
196196 try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi)), 0, eps));
197 try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 2)), 1, eps));
198 try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 4)), 0.7071067811865475, eps));
197 try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 2.0)), 1, eps));
198 try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 4.0)), 0.7071067811865475, eps));
199199 }
200200
201201 {
......@@ -228,8 +228,8 @@ fn testCos() !void {
228228 const eps = epsForType(ty);
229229 try expect(@cos(@as(ty, 0)) == 1);
230230 try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi)), -1, eps));
231 try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 2)), 0, eps));
232 try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 4)), 0.7071067811865475, eps));
231 try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 2.0)), 0, eps));
232 try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 4.0)), 0.7071067811865475, eps));
233233 }
234234
235235 {