diff --git a/doc/langref/test_peer_type_resolution.zig b/doc/langref/test_peer_type_resolution.zig index 9bbac881e3e24576335a448e2661a5dfbb887c5b..de498fc5c80144e4257a3b6ff4dc35a08c0bedb3 100644 --- a/doc/langref/test_peer_type_resolution.zig +++ b/doc/langref/test_peer_type_resolution.zig @@ -10,6 +10,17 @@ test "peer resolve int widening" { try expectEqual(i16, @TypeOf(c)); } +test "peer resolve small int and float" { + // This only works for integer types that can coerce to the float type. + // Larger integer types will cause a compiler error; no float widening occurs. + var i: u8 = 12; + var f: f32 = 34; + _ = .{ &i, &f }; + const x = i + f; + try expectEqual(x, 46.0); + try expectEqual(@TypeOf(x), f32); +} + test "peer resolve arrays of different size to const slice" { try expectEqualStrings("true", boolToStr(true)); try expectEqualStrings("false", boolToStr(false)); diff --git a/src/Sema.zig b/src/Sema.zig index 65381b837525101ec38d7ede3005509e700f399e..78f80b31e2e04df30679104d79d2f8213552c7e1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -27799,15 +27799,7 @@ fn coerceExtra( } const int_info = inst_ty.intInfo(zcu); const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed); - const float_precision: u8 = switch (dest_ty.toIntern()) { - .f16_type => 11, - .f32_type => 24, - .f64_type => 53, - .f80_type => 64, - .f128_type => 113, - else => unreachable, - }; - if (int_precision <= float_precision) { + if (int_precision <= dest_ty.floatSignificandBits(target)) { try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.float_from_int, dest_ty, inst); } @@ -32762,16 +32754,10 @@ fn resolvePeerTypesInner( .fixed_float => { var opt_cur_ty: ?Type = null; - for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { + for (peer_tys, 0..) |opt_ty, i| { const ty = opt_ty orelse continue; switch (ty.zigTypeTag(zcu)) { - .comptime_float, .comptime_int => {}, - .int => { - if (opt_val == null) return .{ .conflict = .{ - .peer_idx_a = strat_reason, - .peer_idx_b = i, - } }; - }, + .comptime_float, .comptime_int, .int => {}, .float => { if (opt_cur_ty) |cur_ty| { if (cur_ty.eql(ty, zcu)) continue; @@ -32798,7 +32784,28 @@ fn resolvePeerTypesInner( // Note that fixed_float is only chosen if there is at least one fixed-width float peer, // so opt_cur_ty must be non-null. - return .{ .success = opt_cur_ty.? }; + const cur_ty = opt_cur_ty.?; + + // Ensure that any integer peers can coerce safely to the resulting float. + for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { + const ty = opt_ty orelse continue; + switch (ty.zigTypeTag(zcu)) { + .comptime_float, .comptime_int, .float => {}, + .int => { + if (opt_val != null) continue; + const int_info = ty.intInfo(zcu); + const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed); + if (int_precision > cur_ty.floatSignificandBits(target)) + return .{ .conflict = .{ + .peer_idx_a = strat_reason, + .peer_idx_b = i, + } }; + }, + else => unreachable, // Previous pass returned on this branch. + } + } + + return .{ .success = cur_ty }; }, .tuple => { diff --git a/src/Type.zig b/src/Type.zig index 475fb09edd9101d99c8ec2b83f39bddef37a3c02..1221f36446127d93025102a1a015f9e379e2dab8 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -1935,6 +1935,18 @@ pub fn floatBits(ty: Type, target: *const Target) u16 { }; } +/// Asserts the type is a fixed-size float or comptime_float. +pub fn floatSignificandBits(ty: Type, target: *const Target) u16 { + return switch (ty.floatBits(target)) { + 16 => 11, + 32 => 24, + 64 => 53, + 80 => 64, + 128 => 113, + else => unreachable, + }; +} + /// Asserts the type is a function or a function pointer. pub fn fnReturnType(ty: Type, zcu: *const Zcu) Type { return Type.fromInterned(zcu.intern_pool.funcTypeReturnType(ty.toIntern())); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 6515df5e2f8f87ed87fa33206b4e388d201b5478..d3f6ea5f48e09231888dbf285642cdb545b741fb 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -175,6 +175,7 @@ test "type coercion from int to float" { var int: Int = std.math.minInt(Int); while (int < std.math.maxInt(Int)) : (int += 1) try value(Float, int); + try value(Float, int); // max } // Check that the min and max values of the integer type can safely be @@ -202,6 +203,8 @@ test "type coercion from int to float" { try check.edgeValues(f128, u113); try check.edgeValues(f128, i114); + try check.value(c_longdouble, @as(u1, 0)); // Smoke test - size varies by target. + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; @@ -1924,6 +1927,47 @@ test "peer type resolution: float and comptime-known fixed-width integer" { try expectEqual(@as(T, 1.234), r2); } +test "peer type resolution: float and runtime-known fixed-width integer" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + + const S = struct { + fn testPeerType(Float: type, Int: type) !void { + var i: Int = 100; + _ = &i; + var f: Float = 1.234; + _ = &f; + comptime assert(@TypeOf(i, f) == Float); + comptime assert(@TypeOf(f, i) == Float); + + var t = true; + _ = &t; + const r1 = if (t) i else f; + const r2 = if (t) f else i; + + try expectEqual(@as(Float, 100.0), r1); + try expectEqual(@as(Float, 1.234), r2); + } + }; + + try S.testPeerType(f16, u11); + try S.testPeerType(f16, i12); + + try S.testPeerType(f32, u24); + try S.testPeerType(f32, i25); + + try S.testPeerType(f64, u53); + try S.testPeerType(f64, i54); + + try S.testPeerType(f80, u64); + try S.testPeerType(f80, i65); + + try S.testPeerType(f128, u113); + try S.testPeerType(f128, i114); + + try S.testPeerType(c_longdouble, u8); // Smoke test - size varies by target. +} + test "peer type resolution: same array type with sentinel" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/cases/compile_errors/add_large_int_and_float.zig b/test/cases/compile_errors/add_large_int_and_float.zig new file mode 100644 index 0000000000000000000000000000000000000000..1faed312b790c04f5c0cc2687587e1bf1932e2f0 --- /dev/null +++ b/test/cases/compile_errors/add_large_int_and_float.zig @@ -0,0 +1,75 @@ +// Test that peer type resolution fails for integer types that cannot safely coerce to a float. + +fn testAdd(Float: type, Int: type) void { + var i: Int = 0; + _ = &i; + var f: Float = 0; + _ = &f; + _ = i + f; + _ = f + i; +} + +export fn entry() void { + testAdd(f16, u11); // Okay + testAdd(f16, u12); // Too big + + testAdd(f16, i12); + testAdd(f16, i13); + + testAdd(f32, u24); + testAdd(f32, u25); + + testAdd(f32, i25); + testAdd(f32, i26); + + testAdd(f64, u53); + testAdd(f64, u54); + + testAdd(f64, i54); + testAdd(f64, i55); + + testAdd(f80, u64); + testAdd(f80, u65); + + testAdd(f80, i65); + testAdd(f80, i66); + + testAdd(f128, u113); + testAdd(f128, u114); + + testAdd(f128, i114); + testAdd(f128, i115); +} + +// error +// +// :8:11: error: incompatible types: 'i115' and 'f128' +// :8:9: note: type 'i115' here +// :8:13: note: type 'f128' here +// :8:11: error: incompatible types: 'i13' and 'f16' +// :8:9: note: type 'i13' here +// :8:13: note: type 'f16' here +// :8:11: error: incompatible types: 'i26' and 'f32' +// :8:9: note: type 'i26' here +// :8:13: note: type 'f32' here +// :8:11: error: incompatible types: 'i55' and 'f64' +// :8:9: note: type 'i55' here +// :8:13: note: type 'f64' here +// :8:11: error: incompatible types: 'i66' and 'f80' +// :8:9: note: type 'i66' here +// :8:13: note: type 'f80' here +// :8:11: error: incompatible types: 'u114' and 'f128' +// :8:9: note: type 'u114' here +// :8:13: note: type 'f128' here +// :8:11: error: incompatible types: 'u12' and 'f16' +// :8:9: note: type 'u12' here +// :8:13: note: type 'f16' here +// :8:11: error: incompatible types: 'u25' and 'f32' +// :8:9: note: type 'u25' here +// :8:13: note: type 'f32' here +// :8:11: error: incompatible types: 'u54' and 'f64' +// :8:9: note: type 'u54' here +// :8:13: note: type 'f64' here +// :8:11: error: incompatible types: 'u65' and 'f80' +// :8:9: note: type 'u65' here +// :8:13: note: type 'f80' here diff --git a/test/cases/compile_errors/coerce_int_to_float.zig b/test/cases/compile_errors/coerce_int_to_float.zig deleted file mode 100644 index d1bf6eb2be077f38245412ee237a9c84f8291fbe..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/coerce_int_to_float.zig +++ /dev/null @@ -1,52 +0,0 @@ -// Test that integer types above a certain size will not coerce to a float. - -fn testCoerce(Float: type, Int: type) void { - var i: Int = 0; - _ = &i; - _ = @as(Float, i); -} - -export fn entry() void { - testCoerce(f16, u11); // Okay - testCoerce(f16, u12); // Too big - - testCoerce(f16, i12); - testCoerce(f16, i13); - - testCoerce(f32, u24); - testCoerce(f32, u25); - - testCoerce(f32, i25); - testCoerce(f32, i26); - - testCoerce(f64, u53); - testCoerce(f64, u54); - - testCoerce(f64, i54); - testCoerce(f64, i55); - - testCoerce(f80, u64); - testCoerce(f80, u65); - - testCoerce(f80, i65); - testCoerce(f80, i66); - - testCoerce(f128, u113); - testCoerce(f128, u114); - - testCoerce(f128, i114); - testCoerce(f128, i115); -} - -// error -// -// :6:20: error: expected type 'f128', found 'i115' -// :6:20: error: expected type 'f128', found 'u114' -// :6:20: error: expected type 'f16', found 'i13' -// :6:20: error: expected type 'f16', found 'u12' -// :6:20: error: expected type 'f32', found 'i26' -// :6:20: error: expected type 'f32', found 'u25' -// :6:20: error: expected type 'f64', found 'i55' -// :6:20: error: expected type 'f64', found 'u54' -// :6:20: error: expected type 'f80', found 'i66' -// :6:20: error: expected type 'f80', found 'u65' diff --git a/test/cases/compile_errors/coerce_large_int_to_float.zig b/test/cases/compile_errors/coerce_large_int_to_float.zig new file mode 100644 index 0000000000000000000000000000000000000000..d1bf6eb2be077f38245412ee237a9c84f8291fbe --- /dev/null +++ b/test/cases/compile_errors/coerce_large_int_to_float.zig @@ -0,0 +1,52 @@ +// Test that integer types above a certain size will not coerce to a float. + +fn testCoerce(Float: type, Int: type) void { + var i: Int = 0; + _ = &i; + _ = @as(Float, i); +} + +export fn entry() void { + testCoerce(f16, u11); // Okay + testCoerce(f16, u12); // Too big + + testCoerce(f16, i12); + testCoerce(f16, i13); + + testCoerce(f32, u24); + testCoerce(f32, u25); + + testCoerce(f32, i25); + testCoerce(f32, i26); + + testCoerce(f64, u53); + testCoerce(f64, u54); + + testCoerce(f64, i54); + testCoerce(f64, i55); + + testCoerce(f80, u64); + testCoerce(f80, u65); + + testCoerce(f80, i65); + testCoerce(f80, i66); + + testCoerce(f128, u113); + testCoerce(f128, u114); + + testCoerce(f128, i114); + testCoerce(f128, i115); +} + +// error +// +// :6:20: error: expected type 'f128', found 'i115' +// :6:20: error: expected type 'f128', found 'u114' +// :6:20: error: expected type 'f16', found 'i13' +// :6:20: error: expected type 'f16', found 'u12' +// :6:20: error: expected type 'f32', found 'i26' +// :6:20: error: expected type 'f32', found 'u25' +// :6:20: error: expected type 'f64', found 'i55' +// :6:20: error: expected type 'f64', found 'u54' +// :6:20: error: expected type 'f80', found 'i66' +// :6:20: error: expected type 'f80', found 'u65'