Skip to content

Commit 04d7b49

Browse files
authored
Merge pull request #24632 from mlugg/lossy-int-to-float-coercion
Sema: compile error on lossy int to float coercion
2 parents 982c387 + 64bf8bb commit 04d7b49

File tree

7 files changed

+68
-36
lines changed

7 files changed

+68
-36
lines changed

lib/std/math.zig

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,11 +1345,15 @@ pub fn lossyCast(comptime T: type, value: anytype) T {
13451345
}
13461346
},
13471347
.float, .comptime_float => {
1348+
// In extreme cases, we probably need a language enhancement to be able to
1349+
// specify a rounding mode here to prevent `@intFromFloat` panics.
1350+
const max: @TypeOf(value) = @floatFromInt(maxInt(T));
1351+
const min: @TypeOf(value) = @floatFromInt(minInt(T));
13481352
if (isNan(value)) {
13491353
return 0;
1350-
} else if (value >= maxInt(T)) {
1354+
} else if (value >= max) {
13511355
return maxInt(T);
1352-
} else if (value <= minInt(T)) {
1356+
} else if (value <= min) {
13531357
return minInt(T);
13541358
} else {
13551359
return @intFromFloat(value);
@@ -1366,7 +1370,7 @@ test lossyCast {
13661370
try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));
13671371
try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));
13681372
try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));
1369-
try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32));
1373+
try testing.expect(lossyCast(u32, @as(f32, @floatFromInt(maxInt(u32)))) == maxInt(u32));
13701374
try testing.expect(lossyCast(u32, nan(f32)) == 0);
13711375
}
13721376

lib/std/math/gamma.zig

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -189,19 +189,19 @@ fn series(comptime T: type, abs: T) T {
189189
2.5066282746310002701649081771338373386264310793408,
190190
};
191191
const denominator = [_]T{
192-
0,
193-
39916800,
194-
120543840,
195-
150917976,
196-
105258076,
197-
45995730,
198-
13339535,
199-
2637558,
200-
357423,
201-
32670,
202-
1925,
203-
66,
204-
1,
192+
0.0,
193+
39916800.0,
194+
120543840.0,
195+
150917976.0,
196+
105258076.0,
197+
45995730.0,
198+
13339535.0,
199+
2637558.0,
200+
357423.0,
201+
32670.0,
202+
1925.0,
203+
66.0,
204+
1.0,
205205
};
206206
var num: T = 0;
207207
var den: T = 0;
@@ -244,9 +244,9 @@ const expectApproxEqRel = std.testing.expectApproxEqRel;
244244
test gamma {
245245
inline for (&.{ f32, f64 }) |T| {
246246
const eps = @sqrt(std.math.floatEps(T));
247-
try expectApproxEqRel(@as(T, 120), gamma(T, 6), eps);
248-
try expectApproxEqRel(@as(T, 362880), gamma(T, 10), eps);
249-
try expectApproxEqRel(@as(T, 6402373705728000), gamma(T, 19), eps);
247+
try expectApproxEqRel(@as(T, 120.0), gamma(T, 6), eps);
248+
try expectApproxEqRel(@as(T, 362880.0), gamma(T, 10), eps);
249+
try expectApproxEqRel(@as(T, 6402373705728000.0), gamma(T, 19), eps);
250250

251251
try expectApproxEqRel(@as(T, 332.7590766955334570), gamma(T, 0.003), eps);
252252
try expectApproxEqRel(@as(T, 1.377260301981044573), gamma(T, 0.654), eps);

lib/std/math/modf.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ fn ModfTests(comptime T: type) type {
7474
r = modf(@as(T, 43874.3));
7575
try expectEqual(43874.0, r.ipart);
7676
// account for precision error
77-
const expected_b: T = 43874.3 - @as(T, 43874);
77+
const expected_b: T = 43874.3 - @as(T, 43874.0);
7878
try expectApproxEqAbs(expected_b, r.fpart, epsilon);
7979

8080
r = modf(@as(T, 1234.340780));

lib/std/math/pow.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ fn isOddInteger(x: f64) bool {
192192
}
193193

194194
test isOddInteger {
195-
try expect(isOddInteger(math.maxInt(i64) * 2) == false);
196-
try expect(isOddInteger(math.maxInt(i64) * 2 + 1) == false);
195+
try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2)) == false);
196+
try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2 + 1)) == false);
197197
try expect(isOddInteger(1 << 53) == false);
198198
try expect(isOddInteger(12.0) == false);
199199
try expect(isOddInteger(15.0) == true);

lib/std/zon/parse.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2774,11 +2774,11 @@ test "std.zon parse float" {
27742774

27752775
// Test big integers
27762776
try std.testing.expectEqual(
2777-
@as(f32, 36893488147419103231),
2777+
@as(f32, 36893488147419103231.0),
27782778
try fromSlice(f32, gpa, "36893488147419103231", null, .{}),
27792779
);
27802780
try std.testing.expectEqual(
2781-
@as(f32, -36893488147419103231),
2781+
@as(f32, -36893488147419103231.0),
27822782
try fromSlice(f32, gpa, "-36893488147419103231", null, .{}),
27832783
);
27842784
try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(
@@ -2788,7 +2788,7 @@ test "std.zon parse float" {
27882788
null,
27892789
.{},
27902790
));
2791-
try std.testing.expectEqual(@as(f32, 0x1ffffffffffffffff), try fromSlice(
2791+
try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice(
27922792
f32,
27932793
gpa,
27942794
"0x1ffffffffffffffff",

src/Sema.zig

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28745,17 +28745,36 @@ fn coerceExtra(
2874528745
break :int;
2874628746
};
2874728747
const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema);
28748-
// TODO implement this compile error
28749-
//const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty);
28750-
//if (!int_again_val.eql(val, inst_ty, zcu)) {
28751-
// return sema.fail(
28752-
// block,
28753-
// inst_src,
28754-
// "type '{f}' cannot represent integer value '{f}'",
28755-
// .{ dest_ty.fmt(pt), val },
28756-
// );
28757-
//}
28758-
return Air.internedToRef(result_val.toIntern());
28748+
const fits: bool = switch (ip.indexToKey(result_val.toIntern())) {
28749+
else => unreachable,
28750+
.undef => true,
28751+
.float => |float| fits: {
28752+
var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
28753+
const operand_big_int = val.toBigInt(&buffer, zcu);
28754+
switch (float.storage) {
28755+
inline else => |x| {
28756+
if (!std.math.isFinite(x)) break :fits false;
28757+
var result_big_int: std.math.big.int.Mutable = .{
28758+
.limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)),
28759+
.len = undefined,
28760+
.positive = undefined,
28761+
};
28762+
switch (result_big_int.setFloat(x, .nearest_even)) {
28763+
.inexact => break :fits false,
28764+
.exact => {},
28765+
}
28766+
break :fits result_big_int.toConst().eql(operand_big_int);
28767+
},
28768+
}
28769+
},
28770+
};
28771+
if (!fits) return sema.fail(
28772+
block,
28773+
inst_src,
28774+
"type '{f}' cannot represent integer value '{f}'",
28775+
.{ dest_ty.fmt(pt), val.fmtValue(pt) },
28776+
);
28777+
return .fromValue(result_val);
2875928778
},
2876028779
else => {},
2876128780
},
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export fn foo() void {
2+
const int: u16 = 65535;
3+
const float: f16 = int;
4+
_ = float;
5+
}
6+
7+
// error
8+
//
9+
// :3:24: error: type 'f16' cannot represent integer value '65535'

0 commit comments

Comments
 (0)