| author | |
| committer | |
| log | 58890066d9caec3d421afe7465a2586668442982 |
| tree | e454d6ebeca73f33486729da0efbd3e27cc3322b |
| parent | 109d01371e56f03183afcae1d2929b347cb4e701 |
| parent | fcf64761d03ec339ab687f9dfcba2a6506804360 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30921
Reviewed-by: Andrew Kelley <andrew@ziglang.org>7 files changed, 219 insertions(+), 70 deletions(-)
doc/langref/test_peer_type_resolution.zig+11| ... | @@ -10,6 +10,17 @@ test "peer resolve int widening" { | ... | @@ -10,6 +10,17 @@ test "peer resolve int widening" { |
| 10 | try expectEqual(i16, @TypeOf(c)); | 10 | try expectEqual(i16, @TypeOf(c)); |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | test "peer resolve small int and float" { | ||
| 14 | // This only works for integer types that can coerce to the float type. | ||
| 15 | // Larger integer types will cause a compiler error; no float widening occurs. | ||
| 16 | var i: u8 = 12; | ||
| 17 | var f: f32 = 34; | ||
| 18 | _ = .{ &i, &f }; | ||
| 19 | const x = i + f; | ||
| 20 | try expectEqual(x, 46.0); | ||
| 21 | try expectEqual(@TypeOf(x), f32); | ||
| 22 | } | ||
| 23 | |||
| 13 | test "peer resolve arrays of different size to const slice" { | 24 | test "peer resolve arrays of different size to const slice" { |
| 14 | try expectEqualStrings("true", boolToStr(true)); | 25 | try expectEqualStrings("true", boolToStr(true)); |
| 15 | try expectEqualStrings("false", boolToStr(false)); | 26 | try expectEqualStrings("false", boolToStr(false)); |
src/Sema.zig+25-18| ... | @@ -27799,15 +27799,7 @@ fn coerceExtra( | ... | @@ -27799,15 +27799,7 @@ fn coerceExtra( |
| 27799 | } | 27799 | } |
| 27800 | const int_info = inst_ty.intInfo(zcu); | 27800 | const int_info = inst_ty.intInfo(zcu); |
| 27801 | const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed); | 27801 | const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed); |
| 27802 | const float_precision: u8 = switch (dest_ty.toIntern()) { | 27802 | if (int_precision <= dest_ty.floatSignificandBits(target)) { |
| 27803 | .f16_type => 11, | ||
| 27804 | .f32_type => 24, | ||
| 27805 | .f64_type => 53, | ||
| 27806 | .f80_type => 64, | ||
| 27807 | .f128_type => 113, | ||
| 27808 | else => unreachable, | ||
| 27809 | }; | ||
| 27810 | if (int_precision <= float_precision) { | ||
| 27811 | try sema.requireRuntimeBlock(block, inst_src, null); | 27803 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 27812 | return block.addTyOp(.float_from_int, dest_ty, inst); | 27804 | return block.addTyOp(.float_from_int, dest_ty, inst); |
| 27813 | } | 27805 | } |
| ... | @@ -32762,16 +32754,10 @@ fn resolvePeerTypesInner( | ... | @@ -32762,16 +32754,10 @@ fn resolvePeerTypesInner( |
| 32762 | .fixed_float => { | 32754 | .fixed_float => { |
| 32763 | var opt_cur_ty: ?Type = null; | 32755 | var opt_cur_ty: ?Type = null; |
| 32764 | 32756 | ||
| 32765 | for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { | 32757 | for (peer_tys, 0..) |opt_ty, i| { |
| 32766 | const ty = opt_ty orelse continue; | 32758 | const ty = opt_ty orelse continue; |
| 32767 | switch (ty.zigTypeTag(zcu)) { | 32759 | switch (ty.zigTypeTag(zcu)) { |
| 32768 | .comptime_float, .comptime_int => {}, | 32760 | .comptime_float, .comptime_int, .int => {}, |
| 32769 | .int => { | ||
| 32770 | if (opt_val == null) return .{ .conflict = .{ | ||
| 32771 | .peer_idx_a = strat_reason, | ||
| 32772 | .peer_idx_b = i, | ||
| 32773 | } }; | ||
| 32774 | }, | ||
| 32775 | .float => { | 32761 | .float => { |
| 32776 | if (opt_cur_ty) |cur_ty| { | 32762 | if (opt_cur_ty) |cur_ty| { |
| 32777 | if (cur_ty.eql(ty, zcu)) continue; | 32763 | if (cur_ty.eql(ty, zcu)) continue; |
| ... | @@ -32798,7 +32784,28 @@ fn resolvePeerTypesInner( | ... | @@ -32798,7 +32784,28 @@ fn resolvePeerTypesInner( |
| 32798 | 32784 | ||
| 32799 | // Note that fixed_float is only chosen if there is at least one fixed-width float peer, | 32785 | // Note that fixed_float is only chosen if there is at least one fixed-width float peer, |
| 32800 | // so opt_cur_ty must be non-null. | 32786 | // so opt_cur_ty must be non-null. |
| 32801 | return .{ .success = opt_cur_ty.? }; | 32787 | const cur_ty = opt_cur_ty.?; |
| 32788 | |||
| 32789 | // Ensure that any integer peers can coerce safely to the resulting float. | ||
| 32790 | for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { | ||
| 32791 | const ty = opt_ty orelse continue; | ||
| 32792 | switch (ty.zigTypeTag(zcu)) { | ||
| 32793 | .comptime_float, .comptime_int, .float => {}, | ||
| 32794 | .int => { | ||
| 32795 | if (opt_val != null) continue; | ||
| 32796 | const int_info = ty.intInfo(zcu); | ||
| 32797 | const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed); | ||
| 32798 | if (int_precision > cur_ty.floatSignificandBits(target)) | ||
| 32799 | return .{ .conflict = .{ | ||
| 32800 | .peer_idx_a = strat_reason, | ||
| 32801 | .peer_idx_b = i, | ||
| 32802 | } }; | ||
| 32803 | }, | ||
| 32804 | else => unreachable, // Previous pass returned on this branch. | ||
| 32805 | } | ||
| 32806 | } | ||
| 32807 | |||
| 32808 | return .{ .success = cur_ty }; | ||
| 32802 | }, | 32809 | }, |
| 32803 | 32810 | ||
| 32804 | .tuple => { | 32811 | .tuple => { |
src/Type.zig+12| ... | @@ -1935,6 +1935,18 @@ pub fn floatBits(ty: Type, target: *const Target) u16 { | ... | @@ -1935,6 +1935,18 @@ pub fn floatBits(ty: Type, target: *const Target) u16 { |
| 1935 | }; | 1935 | }; |
| 1936 | } | 1936 | } |
| 1937 | 1937 | ||
| 1938 | /// Asserts the type is a fixed-size float or comptime_float. | ||
| 1939 | pub fn floatSignificandBits(ty: Type, target: *const Target) u16 { | ||
| 1940 | return switch (ty.floatBits(target)) { | ||
| 1941 | 16 => 11, | ||
| 1942 | 32 => 24, | ||
| 1943 | 64 => 53, | ||
| 1944 | 80 => 64, | ||
| 1945 | 128 => 113, | ||
| 1946 | else => unreachable, | ||
| 1947 | }; | ||
| 1948 | } | ||
| 1949 | |||
| 1938 | /// Asserts the type is a function or a function pointer. | 1950 | /// Asserts the type is a function or a function pointer. |
| 1939 | pub fn fnReturnType(ty: Type, zcu: *const Zcu) Type { | 1951 | pub fn fnReturnType(ty: Type, zcu: *const Zcu) Type { |
| 1940 | return Type.fromInterned(zcu.intern_pool.funcTypeReturnType(ty.toIntern())); | 1952 | return Type.fromInterned(zcu.intern_pool.funcTypeReturnType(ty.toIntern())); |
test/behavior/cast.zig+44| ... | @@ -175,6 +175,7 @@ test "type coercion from int to float" { | ... | @@ -175,6 +175,7 @@ test "type coercion from int to float" { |
| 175 | var int: Int = std.math.minInt(Int); | 175 | var int: Int = std.math.minInt(Int); |
| 176 | while (int < std.math.maxInt(Int)) : (int += 1) | 176 | while (int < std.math.maxInt(Int)) : (int += 1) |
| 177 | try value(Float, int); | 177 | try value(Float, int); |
| 178 | try value(Float, int); // max | ||
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | // Check that the min and max values of the integer type can safely be | 181 | // 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" { | ... | @@ -202,6 +203,8 @@ test "type coercion from int to float" { |
| 202 | try check.edgeValues(f128, u113); | 203 | try check.edgeValues(f128, u113); |
| 203 | try check.edgeValues(f128, i114); | 204 | try check.edgeValues(f128, i114); |
| 204 | 205 | ||
| 206 | try check.value(c_longdouble, @as(u1, 0)); // Smoke test - size varies by target. | ||
| 207 | |||
| 205 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 208 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 206 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 209 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 207 | 210 | ||
| ... | @@ -1924,6 +1927,47 @@ test "peer type resolution: float and comptime-known fixed-width integer" { | ... | @@ -1924,6 +1927,47 @@ test "peer type resolution: float and comptime-known fixed-width integer" { |
| 1924 | try expectEqual(@as(T, 1.234), r2); | 1927 | try expectEqual(@as(T, 1.234), r2); |
| 1925 | } | 1928 | } |
| 1926 | 1929 | ||
| 1930 | test "peer type resolution: float and runtime-known fixed-width integer" { | ||
| 1931 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 1932 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 1933 | |||
| 1934 | const S = struct { | ||
| 1935 | fn testPeerType(Float: type, Int: type) !void { | ||
| 1936 | var i: Int = 100; | ||
| 1937 | _ = &i; | ||
| 1938 | var f: Float = 1.234; | ||
| 1939 | _ = &f; | ||
| 1940 | comptime assert(@TypeOf(i, f) == Float); | ||
| 1941 | comptime assert(@TypeOf(f, i) == Float); | ||
| 1942 | |||
| 1943 | var t = true; | ||
| 1944 | _ = &t; | ||
| 1945 | const r1 = if (t) i else f; | ||
| 1946 | const r2 = if (t) f else i; | ||
| 1947 | |||
| 1948 | try expectEqual(@as(Float, 100.0), r1); | ||
| 1949 | try expectEqual(@as(Float, 1.234), r2); | ||
| 1950 | } | ||
| 1951 | }; | ||
| 1952 | |||
| 1953 | try S.testPeerType(f16, u11); | ||
| 1954 | try S.testPeerType(f16, i12); | ||
| 1955 | |||
| 1956 | try S.testPeerType(f32, u24); | ||
| 1957 | try S.testPeerType(f32, i25); | ||
| 1958 | |||
| 1959 | try S.testPeerType(f64, u53); | ||
| 1960 | try S.testPeerType(f64, i54); | ||
| 1961 | |||
| 1962 | try S.testPeerType(f80, u64); | ||
| 1963 | try S.testPeerType(f80, i65); | ||
| 1964 | |||
| 1965 | try S.testPeerType(f128, u113); | ||
| 1966 | try S.testPeerType(f128, i114); | ||
| 1967 | |||
| 1968 | try S.testPeerType(c_longdouble, u8); // Smoke test - size varies by target. | ||
| 1969 | } | ||
| 1970 | |||
| 1927 | test "peer type resolution: same array type with sentinel" { | 1971 | test "peer type resolution: same array type with sentinel" { |
| 1928 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 1972 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1929 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1973 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/cases/compile_errors/add_large_int_and_float.zig created+75| ... | @@ -0,0 +1,75 @@ | ||
| 1 | // Test that peer type resolution fails for integer types that cannot safely coerce to a float. | ||
| 2 | |||
| 3 | fn testAdd(Float: type, Int: type) void { | ||
| 4 | var i: Int = 0; | ||
| 5 | _ = &i; | ||
| 6 | var f: Float = 0; | ||
| 7 | _ = &f; | ||
| 8 | _ = i + f; | ||
| 9 | _ = f + i; | ||
| 10 | } | ||
| 11 | |||
| 12 | export fn entry() void { | ||
| 13 | testAdd(f16, u11); // Okay | ||
| 14 | testAdd(f16, u12); // Too big | ||
| 15 | |||
| 16 | testAdd(f16, i12); | ||
| 17 | testAdd(f16, i13); | ||
| 18 | |||
| 19 | testAdd(f32, u24); | ||
| 20 | testAdd(f32, u25); | ||
| 21 | |||
| 22 | testAdd(f32, i25); | ||
| 23 | testAdd(f32, i26); | ||
| 24 | |||
| 25 | testAdd(f64, u53); | ||
| 26 | testAdd(f64, u54); | ||
| 27 | |||
| 28 | testAdd(f64, i54); | ||
| 29 | testAdd(f64, i55); | ||
| 30 | |||
| 31 | testAdd(f80, u64); | ||
| 32 | testAdd(f80, u65); | ||
| 33 | |||
| 34 | testAdd(f80, i65); | ||
| 35 | testAdd(f80, i66); | ||
| 36 | |||
| 37 | testAdd(f128, u113); | ||
| 38 | testAdd(f128, u114); | ||
| 39 | |||
| 40 | testAdd(f128, i114); | ||
| 41 | testAdd(f128, i115); | ||
| 42 | } | ||
| 43 | |||
| 44 | // error | ||
| 45 | // | ||
| 46 | // :8:11: error: incompatible types: 'i115' and 'f128' | ||
| 47 | // :8:9: note: type 'i115' here | ||
| 48 | // :8:13: note: type 'f128' here | ||
| 49 | // :8:11: error: incompatible types: 'i13' and 'f16' | ||
| 50 | // :8:9: note: type 'i13' here | ||
| 51 | // :8:13: note: type 'f16' here | ||
| 52 | // :8:11: error: incompatible types: 'i26' and 'f32' | ||
| 53 | // :8:9: note: type 'i26' here | ||
| 54 | // :8:13: note: type 'f32' here | ||
| 55 | // :8:11: error: incompatible types: 'i55' and 'f64' | ||
| 56 | // :8:9: note: type 'i55' here | ||
| 57 | // :8:13: note: type 'f64' here | ||
| 58 | // :8:11: error: incompatible types: 'i66' and 'f80' | ||
| 59 | // :8:9: note: type 'i66' here | ||
| 60 | // :8:13: note: type 'f80' here | ||
| 61 | // :8:11: error: incompatible types: 'u114' and 'f128' | ||
| 62 | // :8:9: note: type 'u114' here | ||
| 63 | // :8:13: note: type 'f128' here | ||
| 64 | // :8:11: error: incompatible types: 'u12' and 'f16' | ||
| 65 | // :8:9: note: type 'u12' here | ||
| 66 | // :8:13: note: type 'f16' here | ||
| 67 | // :8:11: error: incompatible types: 'u25' and 'f32' | ||
| 68 | // :8:9: note: type 'u25' here | ||
| 69 | // :8:13: note: type 'f32' here | ||
| 70 | // :8:11: error: incompatible types: 'u54' and 'f64' | ||
| 71 | // :8:9: note: type 'u54' here | ||
| 72 | // :8:13: note: type 'f64' here | ||
| 73 | // :8:11: error: incompatible types: 'u65' and 'f80' | ||
| 74 | // :8:9: note: type 'u65' here | ||
| 75 | // :8:13: note: type 'f80' here | ||
test/cases/compile_errors/coerce_int_to_float.zig deleted-52| ... | @@ -1,52 +0,0 @@ | ||
| 1 | // Test that integer types above a certain size will not coerce to a float. | ||
| 2 | |||
| 3 | fn testCoerce(Float: type, Int: type) void { | ||
| 4 | var i: Int = 0; | ||
| 5 | _ = &i; | ||
| 6 | _ = @as(Float, i); | ||
| 7 | } | ||
| 8 | |||
| 9 | export fn entry() void { | ||
| 10 | testCoerce(f16, u11); // Okay | ||
| 11 | testCoerce(f16, u12); // Too big | ||
| 12 | |||
| 13 | testCoerce(f16, i12); | ||
| 14 | testCoerce(f16, i13); | ||
| 15 | |||
| 16 | testCoerce(f32, u24); | ||
| 17 | testCoerce(f32, u25); | ||
| 18 | |||
| 19 | testCoerce(f32, i25); | ||
| 20 | testCoerce(f32, i26); | ||
| 21 | |||
| 22 | testCoerce(f64, u53); | ||
| 23 | testCoerce(f64, u54); | ||
| 24 | |||
| 25 | testCoerce(f64, i54); | ||
| 26 | testCoerce(f64, i55); | ||
| 27 | |||
| 28 | testCoerce(f80, u64); | ||
| 29 | testCoerce(f80, u65); | ||
| 30 | |||
| 31 | testCoerce(f80, i65); | ||
| 32 | testCoerce(f80, i66); | ||
| 33 | |||
| 34 | testCoerce(f128, u113); | ||
| 35 | testCoerce(f128, u114); | ||
| 36 | |||
| 37 | testCoerce(f128, i114); | ||
| 38 | testCoerce(f128, i115); | ||
| 39 | } | ||
| 40 | |||
| 41 | // error | ||
| 42 | // | ||
| 43 | // :6:20: error: expected type 'f128', found 'i115' | ||
| 44 | // :6:20: error: expected type 'f128', found 'u114' | ||
| 45 | // :6:20: error: expected type 'f16', found 'i13' | ||
| 46 | // :6:20: error: expected type 'f16', found 'u12' | ||
| 47 | // :6:20: error: expected type 'f32', found 'i26' | ||
| 48 | // :6:20: error: expected type 'f32', found 'u25' | ||
| 49 | // :6:20: error: expected type 'f64', found 'i55' | ||
| 50 | // :6:20: error: expected type 'f64', found 'u54' | ||
| 51 | // :6:20: error: expected type 'f80', found 'i66' | ||
| 52 | // :6:20: error: expected type 'f80', found 'u65' | ||
test/cases/compile_errors/coerce_large_int_to_float.zig created+52| ... | @@ -0,0 +1,52 @@ | ||
| 1 | // Test that integer types above a certain size will not coerce to a float. | ||
| 2 | |||
| 3 | fn testCoerce(Float: type, Int: type) void { | ||
| 4 | var i: Int = 0; | ||
| 5 | _ = &i; | ||
| 6 | _ = @as(Float, i); | ||
| 7 | } | ||
| 8 | |||
| 9 | export fn entry() void { | ||
| 10 | testCoerce(f16, u11); // Okay | ||
| 11 | testCoerce(f16, u12); // Too big | ||
| 12 | |||
| 13 | testCoerce(f16, i12); | ||
| 14 | testCoerce(f16, i13); | ||
| 15 | |||
| 16 | testCoerce(f32, u24); | ||
| 17 | testCoerce(f32, u25); | ||
| 18 | |||
| 19 | testCoerce(f32, i25); | ||
| 20 | testCoerce(f32, i26); | ||
| 21 | |||
| 22 | testCoerce(f64, u53); | ||
| 23 | testCoerce(f64, u54); | ||
| 24 | |||
| 25 | testCoerce(f64, i54); | ||
| 26 | testCoerce(f64, i55); | ||
| 27 | |||
| 28 | testCoerce(f80, u64); | ||
| 29 | testCoerce(f80, u65); | ||
| 30 | |||
| 31 | testCoerce(f80, i65); | ||
| 32 | testCoerce(f80, i66); | ||
| 33 | |||
| 34 | testCoerce(f128, u113); | ||
| 35 | testCoerce(f128, u114); | ||
| 36 | |||
| 37 | testCoerce(f128, i114); | ||
| 38 | testCoerce(f128, i115); | ||
| 39 | } | ||
| 40 | |||
| 41 | // error | ||
| 42 | // | ||
| 43 | // :6:20: error: expected type 'f128', found 'i115' | ||
| 44 | // :6:20: error: expected type 'f128', found 'u114' | ||
| 45 | // :6:20: error: expected type 'f16', found 'i13' | ||
| 46 | // :6:20: error: expected type 'f16', found 'u12' | ||
| 47 | // :6:20: error: expected type 'f32', found 'i26' | ||
| 48 | // :6:20: error: expected type 'f32', found 'u25' | ||
| 49 | // :6:20: error: expected type 'f64', found 'i55' | ||
| 50 | // :6:20: error: expected type 'f64', found 'u54' | ||
| 51 | // :6:20: error: expected type 'f80', found 'i66' | ||
| 52 | // :6:20: error: expected type 'f80', found 'u65' | ||