Merge pull request 'Sema: Support peer type resolution for floats and small integers' (#30921) from jayschwa/zig:ptr-small-int-and-floats into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30921
Reviewed-by: Andrew Kelley <andrew@ziglang.org>
This commit is contained in:
Andrew Kelley
2026-03-12 00:41:05 +01:00
6 changed files with 167 additions and 18 deletions
+11
View File
@@ -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));
+25 -18
View File
@@ -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 => {
+12
View File
@@ -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()));
+44
View File
@@ -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
@@ -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