Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions toolchain/check/cpp/type_mapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@

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 maximum width of 128-bits is returned. Returns
// IntId::None if the argument is not a constant integer or is symbolic.
static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
-> IntId {
auto arg_const_id = context.constant_values().Get(arg_id);
Expand All @@ -39,16 +42,29 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
arg_const_id.is_symbolic()) {
// TODO: Add tests for these cases.
return IntId::None;
;
}
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);
unsigned arg_non_sign_bits = arg_val.getSignificantBits() - 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
unsigned arg_non_sign_bits = arg_val.getSignificantBits() - 1;
int arg_non_sign_bits = arg_val.getSignificantBits() - 1;

nit: I think the google style guide wants use to use int here since the value is clearly in range.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Thanks.


// 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);
// Value doesn't fit to any C++ supported signed integer, diagnose and return
// the maximum supported integer bit width.
if (arg_non_sign_bits >= 128) {
CARBON_DIAGNOSTIC(IntTooLargeForCppType, Error,
"integer value {0} too large to be fitted in any "
"supported signed C++ type, max supported _int128",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about something like this:

Suggested change
"integer value {0} too large to be fitted in any "
"supported signed C++ type, max supported _int128",
"integer value {0} too large to fit in a signed C++ integer type; requires {1} bits, but max is 128",

where {1} is arg_non_sign_bits + 1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sg, done.

TypedInt);
context.emitter().Emit(arg_id, IntTooLargeForCppType,
{.type = arg.type_id, .value = arg_val});
return IntId::MakeRaw(128);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally we want to turn anything where we diagnosed an error into an ErrorInst, which we then use as a signal to avoid diagnosing more errors against the same piece of code.

There is no IntId::Error though, so I understand why you'd make this choice here. The result is that we might diagnose multiple errors against this same integer though.

It's also a little inconsistent with the choice for arg_const_id == SemIR::ErrorInst::ConstantId where if we saw an error we return None, but if we generate an error we return 128.

I think the latter looks the most odd to me. What do you think of returning None here when we make an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sg. As you say it's more consistent with the other error cases, and also I find it good that it doesn't continue with the type i128 to look for the best overload.

How do you find the follow-up message in the test fail_import_large_int_literal.carbon though - call argument of type ``Core.IntLiteral`` is not supported? Shall we add the bit width as well, or the previous message contains enough information about the size?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want two errors for the same piece of code, and we use ErrorInst to signal that something was diagnosed normally. Maybe we need to turn a None into an ErrorInst later, or we need to return an extra bit (an optional maybe?) to signify the difference of returning an error vs returning None in other cases, if nothing was diagnosed (like when it's symbolic?).

For now, we can put a TODO on the second error in the test , saying that we want to get rid of it. And we can follow up to do that in another PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks!

}

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`,
Expand Down
Loading
Loading