Switch to representing FloatLiteralType as a RealId. (#5944)

Don't convert to f64 until we know that's the type that we actually
want. Also reimplement the conversion from RealId to FloatId to perform
an exact conversion with a real check for overflow, rather than
performing an approximate conversion via the host `double` type.

Unfortunately, LLVM doesn't expose its integer mantissa and exponent to
APFloat conversion, so we convert the RealId back to a string for now.

The LLVM conversion also detects overflow only if the literal would
round to having an out-of-range exponent, not if the literal is outside
the range of values of the type as the Carbon design expects. It's not
clear to me which rule we actually want here, so for simplicitly I'm
using the LLVM rule for now.

In preparation for adding other floating-point types beyond f64.
This commit is contained in:
Richard Smith
2025-08-12 22:08:07 +00:00
committed by GitHub
parent 969abfe814
commit 629f77eb61
35 changed files with 352 additions and 380 deletions
@@ -1342,7 +1342,7 @@ fn F() {
// CHECK:STDOUT: %ptr.bcc: type = ptr_type %f64.d77 [concrete]
// CHECK:STDOUT: %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
// CHECK:STDOUT: %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
// CHECK:STDOUT: %float.c15: Core.FloatLiteral = float_value 0.80000000000000004 [concrete]
// CHECK:STDOUT: %float.c64: Core.FloatLiteral = float_literal_value 8e-1 [concrete]
// CHECK:STDOUT: %As.type.6d2: type = facet_type <@As, @As(%f64.d77)> [concrete]
// CHECK:STDOUT: %As.Convert.type.8fc: type = fn_type @As.Convert, @As(%f64.d77) [concrete]
// CHECK:STDOUT: %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
@@ -1353,9 +1353,9 @@ fn F() {
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.6ae: %Core.FloatLiteral.as.As.impl.Convert.type.12c = struct_value () [concrete]
// CHECK:STDOUT: %As.facet: %As.type.6d2 = facet_value Core.FloatLiteral, (%As.impl_witness.a23) [concrete]
// CHECK:STDOUT: %.d03: type = fn_type_with_self_type %As.Convert.type.8fc, %As.facet [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.bound: <bound method> = bound_method %float.c15, %Core.FloatLiteral.as.As.impl.Convert.6ae [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.bound: <bound method> = bound_method %float.c64, %Core.FloatLiteral.as.As.impl.Convert.6ae [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.As.impl.Convert.6ae, @Core.FloatLiteral.as.As.impl.Convert(%int_64) [concrete]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %float.c15, %Core.FloatLiteral.as.As.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %float.c64, %Core.FloatLiteral.as.As.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %float.0fc: %f64.d77 = float_value 0.80000000000000004 [concrete]
// CHECK:STDOUT: %Float.as.Destroy.impl.Op.type.cd5: type = fn_type @Float.as.Destroy.impl.Op, @Float.as.Destroy.impl(%int_64) [concrete]
// CHECK:STDOUT: %Float.as.Destroy.impl.Op.b8c: %Float.as.Destroy.impl.Op.type.cd5 = struct_value () [concrete]
@@ -1384,7 +1384,7 @@ fn F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_value 0.80000000000000004 [concrete = constants.%float.c15]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 8e-1 [concrete = constants.%float.c64]
// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
// CHECK:STDOUT: %f64: type = class_type @Float, @Float(constants.%int_64) [concrete = constants.%f64.d77]
// CHECK:STDOUT: %impl.elem0: %.d03 = impl_witness_access constants.%As.impl_witness.a23, element0 [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.6ae]
@@ -67,7 +67,7 @@ fn F() {
// CHECK:STDOUT: --- fail_todo_import_unsupported_primitive_type.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %float: Core.FloatLiteral = float_value 1.1000000000000001 [concrete]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 11e-1 [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -81,7 +81,7 @@ fn F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_value 1.1000000000000001 [concrete = constants.%float]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 11e-1 [concrete = constants.%float]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -89,7 +89,7 @@ fn F() {
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_value 2 [concrete]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 20e-1 [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -104,7 +104,7 @@ fn F() {
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_value 2 [concrete = constants.%float]
// CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 20e-1 [concrete = constants.%float]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: