Files
carbon-lang/toolchain/check/testdata/builtins/float/add.carbon
T
Richard Smith 629f77eb61 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.
2025-08-12 22:08:07 +00:00

99 lines
3.8 KiB
Plaintext

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/add.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/add.carbon
// --- float_add.carbon
library "[[@TEST_NAME]]";
fn Add(a: f64, b: f64) -> f64 = "float.add";
fn RuntimeCallIsValid(a: f64, b: f64) -> f64 {
//@dump-sem-ir-begin
return Add(a, b);
//@dump-sem-ir-end
}
var x: f64 = Add(2.2, 2.3);
// --- fail_bad_decl.carbon
library "[[@TEST_NAME]]";
// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add" [InvalidBuiltinSignature]
// CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.add";
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
fn TooFew(a: f64) -> f64 = "float.add";
// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add" [InvalidBuiltinSignature]
// CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.add";
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.add";
// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add" [InvalidBuiltinSignature]
// CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.add";
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
fn BadReturnType(a: f64, b: f64) -> bool = "float.add";
fn JustRight(a: f64, b: f64) -> f64 = "float.add";
fn RuntimeCallIsValidTooFew(a: f64) -> f64 {
return TooFew(a);
}
fn RuntimeCallIsValidTooMany(a: f64, b: f64, c: f64) -> f64 {
return TooMany(a, b, c);
}
fn RuntimeCallIsValidBadReturnType(a: f64, b: f64) -> bool {
return BadReturnType(a, b);
}
// --- fail_literal.carbon
library "[[@TEST_NAME]]";
fn Literal() -> type = "float_literal.make_type";
// CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add" [InvalidBuiltinSignature]
// CHECK:STDERR: fn AddLiteral(a: Literal(), b: Literal()) -> Literal() = "float.add";
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
fn AddLiteral(a: Literal(), b: Literal()) -> Literal() = "float.add";
// CHECK:STDOUT: --- float_add.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete]
// CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete]
// CHECK:STDOUT: %Add.type: type = fn_type @Add [concrete]
// CHECK:STDOUT: %Add: %Add.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> %f64.d77 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Add.ref: %Add.type = name_ref Add, file.%Add.decl [concrete = constants.%Add]
// CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a
// CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b
// CHECK:STDOUT: %Add.call: init %f64.d77 = call %Add.ref(%a.ref, %b.ref)
// CHECK:STDOUT: %.loc8_19.1: %f64.d77 = value_of_initializer %Add.call
// CHECK:STDOUT: %.loc8_19.2: %f64.d77 = converted %Add.call, %.loc8_19.1
// CHECK:STDOUT: return %.loc8_19.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: