zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit fcf64761d03ec339ab687f9dfcba2a6506804360 (tree)
parent 89c98e200166aa3e4f5b41859f450d1307216191
Author: Jay Petacat <jay@jayschwa.net>
Date:   Thu, 22 Jan 2026 00:16:14 -0700

Sema: Support peer type resolution for floats and small integers

This builds on the changes in PR #30053 / commit 484cc15366.

Previously, peer type resolution would always result in a conflict for
fixed-width integer and float types. Now that small integer types can
coerce to floats, peer type resolution can take that into account. This
primarily benefits arithmetic with mixed float and integer operands. If
the integer operand can coerce to the float operand's type, it will do
so without requiring an explicit cast. If the integer type can't coerce,
there will be a compiler error; no float widening will occur. Explicit
casting will still be required to make it work.

Diffstat:
Mdoc/langref/test_peer_type_resolution.zig | 11+++++++++++
Msrc/Sema.zig | 33++++++++++++++++++++++++---------
Mtest/behavior/cast.zig | 41+++++++++++++++++++++++++++++++++++++++++
Atest/cases/compile_errors/add_large_int_and_float.zig | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 151 insertions(+), 9 deletions(-)

diff --git 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 @@ -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 @@ -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 @@ -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