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 f9e343f549ac550ab1560091d5d34f481ce2a014..ad4b99781e0fd7503eb7105a22dd3e95f8649603 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -32729,16 +32729,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; @@ -32765,7 +32759,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/test/behavior/cast.zig b/test/behavior/cast.zig index eca43fea44bf3e23a8e6c518a563c5f6f5e12452..d3f6ea5f48e09231888dbf285642cdb545b741fb 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1927,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