Skip to content

Commit 5e0201e

Browse files
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
1 parent 6011040 commit 5e0201e

File tree

3 files changed

+481
-207
lines changed

3 files changed

+481
-207
lines changed

toolchain/check/cpp/type_mapping.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@
2828

2929
namespace Carbon::Check {
3030

31-
// Find the bit width of an integer literal.
32-
// The default bit width is 32. If the literal's bit width is greater than 32,
33-
// the bit width is increased to 64.
31+
// Find the bit width of an integer literal. Following the C++ standard rules
32+
// for assigning a type to a decimal integer literal, the first signed integer
33+
// in which the value could fit among bit widths of 32, 64 and 128 is selected.
34+
// If the value can't fit into a signed integer with width of 128-bits, then a
35+
// diagnostic is emitted and the function returns IntId::None. Returns
36+
// IntId::None also if the argument is not a constant integer, if it is an
37+
// error constant, or if it is a symbolic constant.
3438
static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
3539
-> IntId {
3640
auto arg_const_id = context.constant_values().Get(arg_id);
@@ -42,13 +46,24 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
4246
}
4347
auto arg = context.insts().GetAs<SemIR::IntValue>(
4448
context.constant_values().GetInstId(arg_const_id));
45-
unsigned arg_non_sign_bits =
46-
context.ints().Get(arg.int_id).getSignificantBits() - 1;
49+
llvm::APInt arg_val = context.ints().Get(arg.int_id);
50+
int arg_non_sign_bits = arg_val.getSignificantBits() - 1;
4751

48-
// TODO: What if the literal is larger than 64 bits? Currently an error is
49-
// reported that the int value is too large for type `i64`. Maybe try to fit
50-
// in i128/i256? Try unsigned?
51-
return (arg_non_sign_bits <= 32) ? IntId::MakeRaw(32) : IntId::MakeRaw(64);
52+
if (arg_non_sign_bits >= 128) {
53+
CARBON_DIAGNOSTIC(IntTooLargeForCppType, Error,
54+
"integer value {0} too large to fit in a signed C++ "
55+
"integer type; requires {1} bits, but max is 128",
56+
TypedInt, int);
57+
context.emitter().Emit(arg_id, IntTooLargeForCppType,
58+
{.type = arg.type_id, .value = arg_val},
59+
arg_non_sign_bits + 1);
60+
return IntId::None;
61+
}
62+
63+
return (arg_non_sign_bits < 32)
64+
? IntId::MakeRaw(32)
65+
: ((arg_non_sign_bits < 64) ? IntId::MakeRaw(64)
66+
: IntId::MakeRaw(128));
5267
}
5368

5469
// Attempts to look up a type by name, and returns the corresponding `QualType`,

0 commit comments

Comments
 (0)