fix: require exact contextual integer to float typing
This commit is contained in:
@@ -6,6 +6,7 @@ import "../source"
|
||||
import "../symbol"
|
||||
import "../target"
|
||||
import "../types"
|
||||
import "base:intrinsics"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:slice"
|
||||
@@ -1206,6 +1207,33 @@ fits_integer_type :: proc(value: i128, value_type: types.Type, selected := targe
|
||||
return fits_signed_type(value, value_type, selected) || fits_unsigned_type(value, value_type, selected)
|
||||
}
|
||||
|
||||
integer_fits_float :: proc(value: i128, expected: types.Type, selected: target.Target) -> bool {
|
||||
if !types.is_float(expected, selected) {
|
||||
return false
|
||||
}
|
||||
precision := 0
|
||||
switch types.bits(expected, selected) {
|
||||
case 32: precision = 24
|
||||
case 64: precision = 53
|
||||
case: return false
|
||||
}
|
||||
if value == 0 {
|
||||
return true
|
||||
}
|
||||
magnitude := u128(0)
|
||||
if value < 0 {
|
||||
magnitude = u128(-(value + 1)) + 1
|
||||
} else {
|
||||
magnitude = u128(value)
|
||||
}
|
||||
// Binary floats preserve the significant bits between the first and last one.
|
||||
// Trailing zeros cost no precision, so large powers of two remain exact.
|
||||
// Unsigned magnitude also handles i128's minimum without a float round-trip.
|
||||
significant_bits := 128 - int(intrinsics.count_leading_zeros(magnitude)) -
|
||||
int(intrinsics.count_trailing_zeros(magnitude))
|
||||
return significant_bits <= precision
|
||||
}
|
||||
|
||||
fits_i64 :: proc(value: i128) -> bool {
|
||||
return fits_signed_type(value, types.I64)
|
||||
}
|
||||
@@ -7625,12 +7653,15 @@ build_constant_expr :: proc(
|
||||
numeric_operation := false,
|
||||
) -> hir.Expr_Id {
|
||||
materialized := expected
|
||||
for types.is_optional(materialized, &checker.module.types) {
|
||||
materialized = types.child_type(materialized, &checker.module.types)
|
||||
}
|
||||
nominal := types.INVALID
|
||||
if numeric_operation {
|
||||
if representation, ok := types.distinct_scalar_backing(expected, &checker.module.types);
|
||||
if representation, ok := types.distinct_scalar_backing(materialized, &checker.module.types);
|
||||
ok && !types.is_bool(representation) {
|
||||
nominal = materialized
|
||||
materialized = representation
|
||||
nominal = expected
|
||||
}
|
||||
}
|
||||
recovery_type := types.I64
|
||||
@@ -7640,7 +7671,15 @@ build_constant_expr :: proc(
|
||||
// An integer constant in a float context (e.g. `pi float = 3`) folds to a
|
||||
// float literal, mirroring build_float_expr's bit packing.
|
||||
if constant.kind == .Value && types.is_float(materialized, checker.target) {
|
||||
fval := f64(constant.value) // ponytail: silent precision loss past 2^53, like C int->double
|
||||
if !integer_fits_float(constant.value, materialized, checker.target) {
|
||||
id := source.addf(
|
||||
checker.diagnostics, expr.span,
|
||||
"integer constant %d is not exactly representable in %s",
|
||||
constant.value, type_label(checker, materialized),
|
||||
)
|
||||
return invalid_hir_expr(checker, expr.span, id, expected)
|
||||
}
|
||||
fval := f64(constant.value)
|
||||
bits := transmute(i64)fval
|
||||
if types.bits(materialized, checker.target) == 32 {
|
||||
bits = i64(transmute(u32)f32(fval))
|
||||
|
||||
Reference in New Issue
Block a user