Fix big integer literals type (#6234)

Following the C++ standard rules for assigning a type to a decimal
integer literal, when a Carbon integer literal is passed as a call
argument to a C++ function and it is too big to fit to `int`, `long` or
`long long`, it is assigned an extended integer type (`_int128`).
Discussed in the [interop
meeting](https://docs.google.com/document/d/1YlxEOJ0r-o19o19TCJbFl4Ln1U88yn_Vj23y1Hr5vTk/edit?tab=t.0#heading=h.zdrwb1soj9ms)
and documented in this [design
doc](https://docs.google.com/document/d/18u8z9UEuGH73XzXlDynTgyNK76q1Efl8YYWbpM5LX7o/edit?tab=t.0#heading=h.vxmw1gfg49f2).

Part of #5915
This commit is contained in:
Ivana Ivanovska
2025-10-28 15:01:43 +00:00
committed by GitHub
parent 6011040481
commit 5e0201e5c8
3 changed files with 489 additions and 215 deletions
+24 -9
View File
@@ -28,9 +28,13 @@
namespace Carbon::Check {
// Find the bit width of an integer literal.
// The default bit width is 32. If the literal's bit width is greater than 32,
// the bit width is increased to 64.
// Find the bit width of an integer literal. Following the C++ standard rules
// for assigning a type to a decimal integer literal, the first signed integer
// in which the value could fit among bit widths of 32, 64 and 128 is selected.
// If the value can't fit into a signed integer with width of 128-bits, then a
// diagnostic is emitted and the function returns IntId::None. Returns
// IntId::None also if the argument is not a constant integer, if it is an
// error constant, or if it is a symbolic constant.
static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
-> IntId {
auto arg_const_id = context.constant_values().Get(arg_id);
@@ -42,13 +46,24 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
}
auto arg = context.insts().GetAs<SemIR::IntValue>(
context.constant_values().GetInstId(arg_const_id));
unsigned arg_non_sign_bits =
context.ints().Get(arg.int_id).getSignificantBits() - 1;
llvm::APInt arg_val = context.ints().Get(arg.int_id);
int arg_non_sign_bits = arg_val.getSignificantBits() - 1;
// TODO: What if the literal is larger than 64 bits? Currently an error is
// reported that the int value is too large for type `i64`. Maybe try to fit
// in i128/i256? Try unsigned?
return (arg_non_sign_bits <= 32) ? IntId::MakeRaw(32) : IntId::MakeRaw(64);
if (arg_non_sign_bits >= 128) {
CARBON_DIAGNOSTIC(IntTooLargeForCppType, Error,
"integer value {0} too large to fit in a signed C++ "
"integer type; requires {1} bits, but max is 128",
TypedInt, int);
context.emitter().Emit(arg_id, IntTooLargeForCppType,
{.type = arg.type_id, .value = arg_val},
arg_non_sign_bits + 1);
return IntId::None;
}
return (arg_non_sign_bits < 32)
? IntId::MakeRaw(32)
: ((arg_non_sign_bits < 64) ? IntId::MakeRaw(64)
: IntId::MakeRaw(128));
}
// Attempts to look up a type by name, and returns the corresponding `QualType`,