Implement Core.Negate for float literals (#7616)

Addresses part of issue raised in #7159 by implementing float.negate
builtin for FloatLiteralValues

The following code now compiles:

```
let a: f64 = -1.0;
```

To achieve this I switch the mantissa from being unsigned to signed.
Lexed literals within source file will still always be unsigned, however
it is now possible to create negative FloatLiteralValue constants. Main
non-local changes this causes is:

- All llvm::APInt parsing / printing calls flipped isSigned param
- Zero extension replaced with sign extension
- getActiveBits() replaced with getSignificantBits() for min bit width
calculation
This commit is contained in:
DavidLoftus
2026-08-13 20:28:42 +00:00
committed by GitHub
parent 2784f33221
commit 54b7f1345a
9 changed files with 222 additions and 119 deletions
+8 -1
View File
@@ -6,6 +6,7 @@ package Core library "prelude/operators/arithmetic";
import library "prelude/types/char_literal";
import library "prelude/types/int_literal";
import library "prelude/types/float_literal";
// TODO: Per the design, the associated type `Result` in each of these
// interfaces should have a default value of `Self`:
@@ -120,7 +121,6 @@ impl IntLiteral as SubWith(Self) where .Result = Self {
// Operations for CharLiteral. These need to be here because CharLiteral has no
// associated library of its own.
impl CharLiteral as AddWith(IntLiteral) where .Result = CharLiteral {
fn Op(self, other: IntLiteral) -> CharLiteral = "char_literal.add";
}
@@ -136,3 +136,10 @@ impl CharLiteral as SubWith(IntLiteral) where .Result = CharLiteral {
impl CharLiteral as SubWith(Self) where .Result = IntLiteral {
fn Op(self, other: Self) -> IntLiteral = "char_literal.sub_char";
}
// Operations for FloatLiteral. These need to be here because FloatLiteral has no
// associated library of its own.
impl FloatLiteral as Negate where .Result = Self {
fn Op(self) -> Self = "float.negate";
}
+2 -2
View File
@@ -25,11 +25,11 @@ namespace Carbon {
// TODO: Address this by using a different representation in SemIR.
struct Real : public Printable<Real> {
auto Print(llvm::raw_ostream& output_stream) const -> void {
mantissa.print(output_stream, /*isSigned=*/false);
mantissa.print(output_stream, /*isSigned=*/true);
output_stream << "*" << (is_decimal ? "10" : "2") << "^" << exponent;
}
// The mantissa, represented as an unsigned integer.
// The mantissa, represented as a signed integer.
llvm::APInt mantissa;
// The exponent, represented as a signed integer.
+41 -27
View File
@@ -431,6 +431,19 @@ static auto MakeIntResult(Context& context, SemIR::TypeId type_id,
return MakeIntResult(context, type_id, result);
}
// Converts a Real into a ConstantId.
static auto MakeFloatLiteralResult(Context& context, Real real)
-> SemIR::ConstantId {
auto real_id = context.reals().Add(real);
return MakeConstantResult(
context,
SemIR::FloatLiteralValue{
.type_id =
GetSingletonType(context, SemIR::FloatLiteralType::TypeInstId),
.real_id = real_id},
Phase::Concrete);
}
// Converts an APFloat value into a ConstantId.
static auto MakeFloatResult(Context& context, SemIR::TypeId type_id,
llvm::APFloat value) -> SemIR::ConstantId {
@@ -1438,7 +1451,7 @@ static auto RealToAPFloat(Context& context, RealId real_id,
// Convert the real value to a string.
llvm::SmallString<64> str;
real_value.mantissa.toString(str, real_value.is_decimal ? 10 : 16,
/*signed=*/false, /*formatAsCLiteral=*/true);
/*signed=*/true, /*formatAsCLiteral=*/true);
str += real_value.is_decimal ? "e" : "p";
real_value.exponent.toStringSigned(str);
@@ -1550,20 +1563,10 @@ static auto PerformIntToFloatConvert(Context& context, SemIR::LocId loc_id,
if (!dest_float_type) {
// Target is Core.FloatLiteral, which is always exact.
llvm::APInt mantissa = op_val;
if (src_is_signed && op_val.isNegative()) {
// FloatLiteral can only represent positive real values. Negative
// literals are parsed as Negate(FloatLiteralValue).
context.TODO(loc_id, "negative float literal conversion");
return SemIR::ErrorInst::ConstantId;
}
auto real_id = context.reals().Add(
Real{.mantissa = mantissa,
.exponent = llvm::APInt(32, 0, /*isSigned=*/true),
.is_decimal = true});
return MakeConstantResult(
context,
SemIR::FloatLiteralValue{.type_id = dest_type_id, .real_id = real_id},
Phase::Concrete);
return MakeFloatLiteralResult(
context, Real{.mantissa = mantissa,
.exponent = llvm::APInt(32, 0, /*isSigned=*/true),
.is_decimal = true});
}
llvm::APFloat ap_float(dest_float_type->float_kind.Semantics());
@@ -1702,19 +1705,19 @@ static auto ConvertRealLiteralToInt(Context& context, SemIR::LocId loc_id,
// If the exponent is positive, base^exponent cannot be larger than the result
// size. If it's negative, base^exponent can't be *much* larger than the
// mantissa or we'd have computed a lower bound of 0 bits and bailed out.
CARBON_CHECK(
exponent_upper_bound <=
std::max<unsigned>(mantissa.getActiveBits() * 2, bounds.upper_bound));
CARBON_CHECK(exponent_upper_bound <=
std::max<unsigned>(mantissa.getSignificantBits() * 2,
bounds.upper_bound));
// Compute a bit-width in which we can safely compute the result. We need
// enough space to store the mantissa, base^exponent, the result and a sign
// bit, and the number 10 (4 bits).
unsigned calc_width =
std::max({mantissa.getActiveBits(), exponent_upper_bound,
std::max({mantissa.getSignificantBits(), exponent_upper_bound,
static_cast<unsigned>(bounds.upper_bound + 1), 4U});
// Compute the integer result.
llvm::APInt integer_val = mantissa.zextOrTrunc(calc_width);
llvm::APInt integer_val = mantissa.sextOrTrunc(calc_width);
if (!real_val.is_decimal) {
// Binary exponent (mantissa * 2^exponent).
if (!exponent.isNegative()) {
@@ -2178,16 +2181,27 @@ static auto PerformBuiltinUnaryFloatOp(Context& context,
SemIR::BuiltinFunctionKind builtin_kind,
SemIR::InstId arg_id)
-> SemIR::ConstantId {
CARBON_CHECK(builtin_kind == SemIR::BuiltinFunctionKind::FloatNegate,
"Unexpected builtin kind");
if (auto literal =
context.insts().TryGetAs<SemIR::FloatLiteralValue>(arg_id)) {
auto real_val = context.reals().Get(literal->real_id);
// Check if negation would overflow.
if (real_val.mantissa.isMinSignedValue()) {
real_val.mantissa =
real_val.mantissa.sext(real_val.mantissa.getBitWidth() + 1);
}
real_val.mantissa.negate();
return MakeFloatLiteralResult(context, std::move(real_val));
}
auto op = context.insts().GetAs<SemIR::FloatValue>(arg_id);
auto op_val = context.floats().Get(op.float_id);
switch (builtin_kind) {
case SemIR::BuiltinFunctionKind::FloatNegate:
op_val.changeSign();
break;
default:
CARBON_FATAL("Unexpected builtin kind");
}
op_val.changeSign();
return MakeFloatResult(context, op.type_id, std::move(op_val));
}
@@ -0,0 +1,166 @@
// 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_literal/negate.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float_literal/negate.carbon
library "[[@TEST_NAME]]";
fn Negate(a: Core.FloatLiteral) -> Core.FloatLiteral = "float.negate";
//@dump-sem-ir-begin
// Positive values become negative
let v1: Core.FloatLiteral = Negate(1.0);
let v2: Core.FloatLiteral = Negate(1.8e9);
// Negative values become positive
let v3: Core.FloatLiteral = Negate(Negate(87.87));
let v4: Core.FloatLiteral = Negate(Negate(0x1.8p1));
// Zero is still zero
let v5: Core.FloatLiteral = Negate(0.0);
let v6: Core.FloatLiteral = Negate(Negate(0.0));
//@dump-sem-ir-end
// CHECK:STDOUT: --- negate.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %pattern_type: type = pattern_type Core.FloatLiteral [concrete]
// CHECK:STDOUT: %Negate.type: type = fn_type @Negate [concrete]
// CHECK:STDOUT: %Negate: %Negate.type = struct_value () [concrete]
// CHECK:STDOUT: %v1.patt: %pattern_type = value_binding_pattern v1 [concrete]
// CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete]
// CHECK:STDOUT: %float.84a: Core.FloatLiteral = float_literal_value -10e-1 [concrete]
// CHECK:STDOUT: %v2.patt: %pattern_type = value_binding_pattern v2 [concrete]
// CHECK:STDOUT: %float.c59: Core.FloatLiteral = float_literal_value 18e8 [concrete]
// CHECK:STDOUT: %float.838: Core.FloatLiteral = float_literal_value -18e8 [concrete]
// CHECK:STDOUT: %v3.patt: %pattern_type = value_binding_pattern v3 [concrete]
// CHECK:STDOUT: %float.b21268.1: Core.FloatLiteral = float_literal_value 8787e-2 [concrete]
// CHECK:STDOUT: %float.923: Core.FloatLiteral = float_literal_value -8787e-2 [concrete]
// CHECK:STDOUT: %float.b21268.2: Core.FloatLiteral = float_literal_value 8787e-2 [concrete]
// CHECK:STDOUT: %v4.patt: %pattern_type = value_binding_pattern v4 [concrete]
// CHECK:STDOUT: %float.eda197.1: Core.FloatLiteral = float_literal_value 24p-3 [concrete]
// CHECK:STDOUT: %float.0b3: Core.FloatLiteral = float_literal_value -24p-3 [concrete]
// CHECK:STDOUT: %float.eda197.2: Core.FloatLiteral = float_literal_value 24p-3 [concrete]
// CHECK:STDOUT: %v5.patt: %pattern_type = value_binding_pattern v5 [concrete]
// CHECK:STDOUT: %float.1f75e4.1: Core.FloatLiteral = float_literal_value 0e-1 [concrete]
// CHECK:STDOUT: %float.1f75e4.2: Core.FloatLiteral = float_literal_value 0e-1 [concrete]
// CHECK:STDOUT: %v6.patt: %pattern_type = value_binding_pattern v6 [concrete]
// CHECK:STDOUT: %float.1f75e4.3: Core.FloatLiteral = float_literal_value 0e-1 [concrete]
// CHECK:STDOUT: %float.1f75e4.4: Core.FloatLiteral = float_literal_value 0e-1 [concrete]
// CHECK:STDOUT: %float.1f75e4.5: Core.FloatLiteral = float_literal_value 0e-1 [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: .FloatLiteral = %Core.FloatLiteral
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.FloatLiteral: type = import_ref Core//prelude/parts/float_literal, FloatLiteral, loaded [concrete = Core.FloatLiteral]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: %.loc19_39.1: Core.FloatLiteral = value_of_initializer @__global_init.%Negate.call.loc19 [concrete = constants.%float.84a]
// CHECK:STDOUT: %.loc19_39.2: Core.FloatLiteral = converted @__global_init.%Negate.call.loc19, %.loc19_39.1 [concrete = constants.%float.84a]
// CHECK:STDOUT: %.loc19_13: type = splice_block %FloatLiteral.ref.loc19 [concrete = Core.FloatLiteral] {
// CHECK:STDOUT: %Core.ref.loc19: <namespace> = name_ref Core, imports.%Core [concrete = imports.%Core]
// CHECK:STDOUT: %FloatLiteral.ref.loc19: type = name_ref FloatLiteral, imports.%Core.FloatLiteral [concrete = Core.FloatLiteral]
// CHECK:STDOUT: }
// CHECK:STDOUT: %v1: Core.FloatLiteral = wrapper_binding v1, %.loc19_39.2
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %v1.patt: %pattern_type = value_binding_pattern v1 [concrete = constants.%v1.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc20_41.1: Core.FloatLiteral = value_of_initializer @__global_init.%Negate.call.loc20 [concrete = constants.%float.838]
// CHECK:STDOUT: %.loc20_41.2: Core.FloatLiteral = converted @__global_init.%Negate.call.loc20, %.loc20_41.1 [concrete = constants.%float.838]
// CHECK:STDOUT: %.loc20_13: type = splice_block %FloatLiteral.ref.loc20 [concrete = Core.FloatLiteral] {
// CHECK:STDOUT: %Core.ref.loc20: <namespace> = name_ref Core, imports.%Core [concrete = imports.%Core]
// CHECK:STDOUT: %FloatLiteral.ref.loc20: type = name_ref FloatLiteral, imports.%Core.FloatLiteral [concrete = Core.FloatLiteral]
// CHECK:STDOUT: }
// CHECK:STDOUT: %v2: Core.FloatLiteral = wrapper_binding v2, %.loc20_41.2
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %v2.patt: %pattern_type = value_binding_pattern v2 [concrete = constants.%v2.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc23_49.1: Core.FloatLiteral = value_of_initializer @__global_init.%Negate.call.loc23_49 [concrete = constants.%float.b21268.2]
// CHECK:STDOUT: %.loc23_49.2: Core.FloatLiteral = converted @__global_init.%Negate.call.loc23_49, %.loc23_49.1 [concrete = constants.%float.b21268.2]
// CHECK:STDOUT: %.loc23_13: type = splice_block %FloatLiteral.ref.loc23 [concrete = Core.FloatLiteral] {
// CHECK:STDOUT: %Core.ref.loc23: <namespace> = name_ref Core, imports.%Core [concrete = imports.%Core]
// CHECK:STDOUT: %FloatLiteral.ref.loc23: type = name_ref FloatLiteral, imports.%Core.FloatLiteral [concrete = Core.FloatLiteral]
// CHECK:STDOUT: }
// CHECK:STDOUT: %v3: Core.FloatLiteral = wrapper_binding v3, %.loc23_49.2
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %v3.patt: %pattern_type = value_binding_pattern v3 [concrete = constants.%v3.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc24_51.1: Core.FloatLiteral = value_of_initializer @__global_init.%Negate.call.loc24_51 [concrete = constants.%float.eda197.2]
// CHECK:STDOUT: %.loc24_51.2: Core.FloatLiteral = converted @__global_init.%Negate.call.loc24_51, %.loc24_51.1 [concrete = constants.%float.eda197.2]
// CHECK:STDOUT: %.loc24_13: type = splice_block %FloatLiteral.ref.loc24 [concrete = Core.FloatLiteral] {
// CHECK:STDOUT: %Core.ref.loc24: <namespace> = name_ref Core, imports.%Core [concrete = imports.%Core]
// CHECK:STDOUT: %FloatLiteral.ref.loc24: type = name_ref FloatLiteral, imports.%Core.FloatLiteral [concrete = Core.FloatLiteral]
// CHECK:STDOUT: }
// CHECK:STDOUT: %v4: Core.FloatLiteral = wrapper_binding v4, %.loc24_51.2
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %v4.patt: %pattern_type = value_binding_pattern v4 [concrete = constants.%v4.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc27_39.1: Core.FloatLiteral = value_of_initializer @__global_init.%Negate.call.loc27 [concrete = constants.%float.1f75e4.2]
// CHECK:STDOUT: %.loc27_39.2: Core.FloatLiteral = converted @__global_init.%Negate.call.loc27, %.loc27_39.1 [concrete = constants.%float.1f75e4.2]
// CHECK:STDOUT: %.loc27_13: type = splice_block %FloatLiteral.ref.loc27 [concrete = Core.FloatLiteral] {
// CHECK:STDOUT: %Core.ref.loc27: <namespace> = name_ref Core, imports.%Core [concrete = imports.%Core]
// CHECK:STDOUT: %FloatLiteral.ref.loc27: type = name_ref FloatLiteral, imports.%Core.FloatLiteral [concrete = Core.FloatLiteral]
// CHECK:STDOUT: }
// CHECK:STDOUT: %v5: Core.FloatLiteral = wrapper_binding v5, %.loc27_39.2
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %v5.patt: %pattern_type = value_binding_pattern v5 [concrete = constants.%v5.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc28_47.1: Core.FloatLiteral = value_of_initializer @__global_init.%Negate.call.loc28_47 [concrete = constants.%float.1f75e4.5]
// CHECK:STDOUT: %.loc28_47.2: Core.FloatLiteral = converted @__global_init.%Negate.call.loc28_47, %.loc28_47.1 [concrete = constants.%float.1f75e4.5]
// CHECK:STDOUT: %.loc28_13: type = splice_block %FloatLiteral.ref.loc28 [concrete = Core.FloatLiteral] {
// CHECK:STDOUT: %Core.ref.loc28: <namespace> = name_ref Core, imports.%Core [concrete = imports.%Core]
// CHECK:STDOUT: %FloatLiteral.ref.loc28: type = name_ref FloatLiteral, imports.%Core.FloatLiteral [concrete = Core.FloatLiteral]
// CHECK:STDOUT: }
// CHECK:STDOUT: %v6: Core.FloatLiteral = wrapper_binding v6, %.loc28_47.2
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %v6.patt: %pattern_type = value_binding_pattern v6 [concrete = constants.%v6.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Negate.ref.loc19: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %float.loc19: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da]
// CHECK:STDOUT: %Negate.call.loc19: init Core.FloatLiteral = call %Negate.ref.loc19(%float.loc19) [concrete = constants.%float.84a]
// CHECK:STDOUT: %Negate.ref.loc20: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %float.loc20: Core.FloatLiteral = float_literal_value 18e8 [concrete = constants.%float.c59]
// CHECK:STDOUT: %Negate.call.loc20: init Core.FloatLiteral = call %Negate.ref.loc20(%float.loc20) [concrete = constants.%float.838]
// CHECK:STDOUT: %Negate.ref.loc23_29: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %Negate.ref.loc23_36: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %float.loc23: Core.FloatLiteral = float_literal_value 8787e-2 [concrete = constants.%float.b21268.1]
// CHECK:STDOUT: %Negate.call.loc23_48: init Core.FloatLiteral = call %Negate.ref.loc23_36(%float.loc23) [concrete = constants.%float.923]
// CHECK:STDOUT: %.loc23_48.1: Core.FloatLiteral = value_of_initializer %Negate.call.loc23_48 [concrete = constants.%float.923]
// CHECK:STDOUT: %.loc23_48.2: Core.FloatLiteral = converted %Negate.call.loc23_48, %.loc23_48.1 [concrete = constants.%float.923]
// CHECK:STDOUT: %Negate.call.loc23_49: init Core.FloatLiteral = call %Negate.ref.loc23_29(%.loc23_48.2) [concrete = constants.%float.b21268.2]
// CHECK:STDOUT: %Negate.ref.loc24_29: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %Negate.ref.loc24_36: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %float.loc24: Core.FloatLiteral = float_literal_value 24p-3 [concrete = constants.%float.eda197.1]
// CHECK:STDOUT: %Negate.call.loc24_50: init Core.FloatLiteral = call %Negate.ref.loc24_36(%float.loc24) [concrete = constants.%float.0b3]
// CHECK:STDOUT: %.loc24_50.1: Core.FloatLiteral = value_of_initializer %Negate.call.loc24_50 [concrete = constants.%float.0b3]
// CHECK:STDOUT: %.loc24_50.2: Core.FloatLiteral = converted %Negate.call.loc24_50, %.loc24_50.1 [concrete = constants.%float.0b3]
// CHECK:STDOUT: %Negate.call.loc24_51: init Core.FloatLiteral = call %Negate.ref.loc24_29(%.loc24_50.2) [concrete = constants.%float.eda197.2]
// CHECK:STDOUT: %Negate.ref.loc27: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %float.loc27: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f75e4.1]
// CHECK:STDOUT: %Negate.call.loc27: init Core.FloatLiteral = call %Negate.ref.loc27(%float.loc27) [concrete = constants.%float.1f75e4.2]
// CHECK:STDOUT: %Negate.ref.loc28_29: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %Negate.ref.loc28_36: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate]
// CHECK:STDOUT: %float.loc28: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f75e4.3]
// CHECK:STDOUT: %Negate.call.loc28_46: init Core.FloatLiteral = call %Negate.ref.loc28_36(%float.loc28) [concrete = constants.%float.1f75e4.4]
// CHECK:STDOUT: %.loc28_46.1: Core.FloatLiteral = value_of_initializer %Negate.call.loc28_46 [concrete = constants.%float.1f75e4.4]
// CHECK:STDOUT: %.loc28_46.2: Core.FloatLiteral = converted %Negate.call.loc28_46, %.loc28_46.1 [concrete = constants.%float.1f75e4.4]
// CHECK:STDOUT: %Negate.call.loc28_47: init Core.FloatLiteral = call %Negate.ref.loc28_29(%.loc28_46.2) [concrete = constants.%float.1f75e4.5]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -55,18 +55,6 @@ import library "int_to_float";
let overflow: f64 = ConvertIntLiteralToF64(1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000);
//@dump-sem-ir-end
// --- fail_todo_negative_literal.carbon
library "[[@TEST_NAME]]";
import library "int_to_float";
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_negative_literal.carbon:[[@LINE+4]]:20: error: semantics TODO: `negative float literal conversion` [SemanticsTodo]
// CHECK:STDERR: let invalid: f64 = ConvertIntLiteralToFloatLiteral(-1) as f64;
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
let invalid: f64 = ConvertIntLiteralToFloatLiteral(-1) as f64;
//@dump-sem-ir-end
// CHECK:STDOUT: --- fail_overflow.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
@@ -101,75 +89,3 @@ let invalid: f64 = ConvertIntLiteralToFloatLiteral(-1) as f64;
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_negative_literal.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete]
// CHECK:STDOUT: %f64.dc1: type = class_type @Float, @Float(%int_64) [concrete]
// CHECK:STDOUT: %pattern_type.fb7: type = pattern_type %f64.dc1 [concrete]
// CHECK:STDOUT: %invalid.patt: %pattern_type.fb7 = value_binding_pattern invalid [concrete]
// CHECK:STDOUT: %ConvertIntLiteralToFloatLiteral.type: type = fn_type @ConvertIntLiteralToFloatLiteral [concrete]
// CHECK:STDOUT: %ConvertIntLiteralToFloatLiteral: %ConvertIntLiteralToFloatLiteral.type = struct_value () [concrete]
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete]
// CHECK:STDOUT: %Negate.type: type = facet_type <@Negate> [concrete]
// CHECK:STDOUT: %Negate.impl_witness: <witness> = impl_witness imports.%Negate.impl_witness_table [concrete]
// CHECK:STDOUT: %Negate.facet: %Negate.type = facet_value Core.IntLiteral, (%Negate.impl_witness) [concrete]
// CHECK:STDOUT: %Negate.WithSelf.Op.type.e85: type = fn_type @Negate.WithSelf.Op, @Negate.WithSelf(%Negate.facet) [concrete]
// CHECK:STDOUT: %.758: type = fn_type_with_self_type %Negate.WithSelf.Op.type.e85, %Negate.facet [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.Negate.impl.Op.type: type = fn_type @Core.IntLiteral.as.Negate.impl.Op [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.Negate.impl.Op: %Core.IntLiteral.as.Negate.impl.Op.type = struct_value () [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.Negate.impl.Op.bound: <bound method> = bound_method %int_1, %Core.IntLiteral.as.Negate.impl.Op [concrete]
// CHECK:STDOUT: %int_-1: Core.IntLiteral = int_value -1 [concrete]
// CHECK:STDOUT: %As.type.0e0: type = facet_type <@As, @As(%f64.dc1)> [concrete]
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.type.69a: type = fn_type @Core.FloatLiteral.as.As.impl.Convert, @Core.FloatLiteral.as.As.impl(%To) [symbolic]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.829: %Core.FloatLiteral.as.As.impl.Convert.type.69a = struct_value () [symbolic]
// CHECK:STDOUT: %As.impl_witness.355: <witness> = impl_witness imports.%As.impl_witness_table, @Core.FloatLiteral.as.As.impl(%int_64) [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.type.0cf: type = fn_type @Core.FloatLiteral.as.As.impl.Convert, @Core.FloatLiteral.as.As.impl(%int_64) [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.20d: %Core.FloatLiteral.as.As.impl.Convert.type.0cf = struct_value () [concrete]
// CHECK:STDOUT: %As.facet: %As.type.0e0 = facet_value Core.FloatLiteral, (%As.impl_witness.355) [concrete]
// CHECK:STDOUT: %As.WithSelf.Convert.type.a43: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%f64.dc1, %As.facet) [concrete]
// CHECK:STDOUT: %.a08: type = fn_type_with_self_type %As.WithSelf.Convert.type.a43, %As.facet [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.As.impl.Convert.20d, @Core.FloatLiteral.as.As.impl.Convert(%int_64) [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Main.ConvertIntLiteralToFloatLiteral: %ConvertIntLiteralToFloatLiteral.type = import_ref Main//int_to_float, ConvertIntLiteralToFloatLiteral, loaded [concrete = constants.%ConvertIntLiteralToFloatLiteral]
// CHECK:STDOUT: %Core.import_ref.abd = import_ref Core//prelude/parts/int_literal, loc{{\d+_\d+}}, unloaded
// CHECK:STDOUT: %Core.import_ref.9c3: %Core.IntLiteral.as.Negate.impl.Op.type = import_ref Core//prelude/parts/int_literal, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.Negate.impl.Op]
// CHECK:STDOUT: %Negate.impl_witness_table = impl_witness_table (%Core.import_ref.abd, %Core.import_ref.9c3), @Core.IntLiteral.as.Negate.impl [concrete]
// CHECK:STDOUT: %Core.import_ref.c06: @Core.FloatLiteral.as.As.impl.%Core.FloatLiteral.as.As.impl.Convert.type (%Core.FloatLiteral.as.As.impl.Convert.type.69a) = import_ref Core//prelude/parts/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.As.impl.%Core.FloatLiteral.as.As.impl.Convert (constants.%Core.FloatLiteral.as.As.impl.Convert.829)]
// CHECK:STDOUT: %As.impl_witness_table = impl_witness_table (%Core.import_ref.c06), @Core.FloatLiteral.as.As.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: %f64: type = type_literal constants.%f64.dc1 [concrete = constants.%f64.dc1]
// CHECK:STDOUT: %invalid: %f64.dc1 = wrapper_binding invalid, @__global_init.%.loc9_56.2 [concrete = <error>]
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %invalid.patt: %pattern_type.fb7 = value_binding_pattern invalid [concrete = constants.%invalid.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %ConvertIntLiteralToFloatLiteral.ref: %ConvertIntLiteralToFloatLiteral.type = name_ref ConvertIntLiteralToFloatLiteral, imports.%Main.ConvertIntLiteralToFloatLiteral [concrete = constants.%ConvertIntLiteralToFloatLiteral]
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
// CHECK:STDOUT: %impl.elem1: %.758 = impl_witness_access constants.%Negate.impl_witness, element1 [concrete = constants.%Core.IntLiteral.as.Negate.impl.Op]
// CHECK:STDOUT: %bound_method.loc9_52: <bound method> = bound_method %int_1, %impl.elem1 [concrete = constants.%Core.IntLiteral.as.Negate.impl.Op.bound]
// CHECK:STDOUT: %Core.IntLiteral.as.Negate.impl.Op.call: init Core.IntLiteral = call %bound_method.loc9_52(%int_1) [concrete = constants.%int_-1]
// CHECK:STDOUT: %.loc9_52.1: Core.IntLiteral = value_of_initializer %Core.IntLiteral.as.Negate.impl.Op.call [concrete = constants.%int_-1]
// CHECK:STDOUT: %.loc9_52.2: Core.IntLiteral = converted %Core.IntLiteral.as.Negate.impl.Op.call, %.loc9_52.1 [concrete = constants.%int_-1]
// CHECK:STDOUT: %ConvertIntLiteralToFloatLiteral.call: init Core.FloatLiteral = call %ConvertIntLiteralToFloatLiteral.ref(%.loc9_52.2) [concrete = <error>]
// CHECK:STDOUT: %f64: type = type_literal constants.%f64.dc1 [concrete = constants.%f64.dc1]
// CHECK:STDOUT: %impl.elem0: %.a08 = impl_witness_access constants.%As.impl_witness.355, element0 [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.20d]
// CHECK:STDOUT: %bound_method.loc9_56.1: <bound method> = bound_method %ConvertIntLiteralToFloatLiteral.call, %impl.elem0 [concrete = <error>]
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.FloatLiteral.as.As.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.specific_fn]
// CHECK:STDOUT: %bound_method.loc9_56.2: <bound method> = bound_method %ConvertIntLiteralToFloatLiteral.call, %specific_fn [concrete = <error>]
// CHECK:STDOUT: %.loc9_54.1: Core.FloatLiteral = value_of_initializer %ConvertIntLiteralToFloatLiteral.call [concrete = <error>]
// CHECK:STDOUT: %.loc9_54.2: Core.FloatLiteral = converted %ConvertIntLiteralToFloatLiteral.call, %.loc9_54.1 [concrete = <error>]
// CHECK:STDOUT: %Core.FloatLiteral.as.As.impl.Convert.call: init %f64.dc1 = call %bound_method.loc9_56.2(%.loc9_54.2) [concrete = <error>]
// CHECK:STDOUT: %.loc9_56.1: %f64.dc1 = value_of_initializer %Core.FloatLiteral.as.As.impl.Convert.call [concrete = <error>]
// CHECK:STDOUT: %.loc9_56.2: %f64.dc1 = converted %ConvertIntLiteralToFloatLiteral.call, %.loc9_56.1 [concrete = <error>]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
+1 -1
View File
@@ -325,7 +325,7 @@ auto NumericLiteral::Parser::GetMantissa() -> llvm::APInt {
const char* end = IsInt() ? int_part_.end() : fract_part_.end();
llvm::StringRef digits(int_part_.begin(), end - int_part_.begin());
return ParseInt(digits, radix_, mantissa_needs_cleaning_,
/*is_signed=*/false);
/*is_signed=*/true);
}
auto NumericLiteral::Parser::GetExponent() -> llvm::APInt {
+1 -1
View File
@@ -253,7 +253,7 @@ TEST_F(LexerTest, HandlesNumericLiteral) {
auto token_1_5e9 = token_1_234_567 + 1;
auto value_1_5e9 =
value_stores.reals().Get(buffer.GetRealLiteral(*token_1_5e9));
EXPECT_EQ(value_1_5e9.mantissa.getZExtValue(), 15);
EXPECT_EQ(value_1_5e9.mantissa.getSExtValue(), 15);
EXPECT_EQ(value_1_5e9.exponent.getSExtValue(), 8);
EXPECT_EQ(value_1_5e9.is_decimal, true);
}
+2 -2
View File
@@ -724,8 +724,8 @@ constexpr BuiltinInfo IntGreaterEq = {
"int.greater_eq", ValidateSignature<auto(IntT, IntU)->Bool>};
// "float.negate": float negation.
constexpr BuiltinInfo FloatNegate = {
"float.negate", ValidateSignature<auto(SizedFloatT)->SizedFloatT>};
constexpr BuiltinInfo FloatNegate = {"float.negate",
ValidateSignature<auto(FloatT)->FloatT>};
// "float.add": float addition.
constexpr BuiltinInfo FloatAdd = {
+1 -1
View File
@@ -1705,7 +1705,7 @@ auto Formatter::FormatArg(ExprRegionId id) -> void {
auto Formatter::FormatArg(RealId id) -> void {
// TODO: Format with a `.` when the exponent is near zero.
const auto& real = sem_ir_->reals().Get(id);
real.mantissa.print(out(), /*isSigned=*/false);
real.mantissa.print(out(), /*isSigned=*/true);
out() << (real.is_decimal ? 'e' : 'p') << real.exponent;
}