mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-26 13:01:34 +03:00
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.
This commit is contained in:
@@ -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));
|
||||
|
||||
+24
-9
@@ -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 => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user