Recover when converting to an invalid integer type (#7342)

Converting an integer value to a destination type that is not a valid
integer type -- such as `i8388609` or `i16777216`, whose bit widths are
diagnosed as invalid -- hit a CHECK failure in
`TypeStore::GetIntTypeInfo` during constant evaluation of `int.convert`
/ `int.convert_checked`:

```
CHECK failure at toolchain/sem_ir/type.cpp:189: int_info: Type type(...) is not an integer type
```

The width error is already diagnosed when forming the type
(`IntWidthNotMultipleOf8` / `IntWidthTooLarge`), so `PerformIntConvert`
and `PerformCheckedIntConvert` now use `TryGetIntTypeInfo` and produce
an error value instead of crashing, following the existing
`SemIR::ErrorInst::ConstantId` convention in `eval.cpp`.

Added a file test covering all three reproducers from the issue
(`i8388609`, `i16777216`, `Core.Int(8388609)`); it crashes without this
change. The full `//toolchain/testing:file_test` suite (1578 tests)
passes.

Fixes #7278.

I have reviewed this change and take responsibility for it.

Assisted-by: Claude
This commit is contained in:
mstr-six
2026-06-12 21:19:57 +00:00
committed by GitHub
parent afd679129d
commit 250da35c3b
2 changed files with 71 additions and 4 deletions
+14 -4
View File
@@ -1327,8 +1327,14 @@ static auto PerformIntConvert(Context& context, SemIR::InstId arg_id,
SemIR::TypeId dest_type_id) -> SemIR::ConstantId {
auto arg_val =
context.ints().Get(context.insts().GetAs<SemIR::IntValue>(arg_id).int_id);
auto [dest_is_signed, bit_width_id] =
context.sem_ir().types().GetIntTypeInfo(dest_type_id);
auto dest_int_info = context.sem_ir().types().TryGetIntTypeInfo(dest_type_id);
if (!dest_int_info) {
// The destination is not a valid integer type, such as when its bit width
// was diagnosed as invalid. The error was already diagnosed when forming
// the type, so just produce an error value.
return SemIR::ErrorInst::ConstantId;
}
auto [dest_is_signed, bit_width_id] = *dest_int_info;
if (bit_width_id.has_value()) {
// TODO: If the value fits in the destination type, reuse the existing
// int_id rather than recomputing it. This is probably the most common case.
@@ -1351,8 +1357,12 @@ static auto PerformCheckedIntConvert(Context& context, SemIR::LocId loc_id,
auto arg = context.insts().GetAs<SemIR::IntValue>(arg_id);
auto arg_val = context.ints().Get(arg.int_id);
auto [is_signed, bit_width_id] =
context.sem_ir().types().GetIntTypeInfo(dest_type_id);
auto dest_int_info = context.sem_ir().types().TryGetIntTypeInfo(dest_type_id);
if (!dest_int_info) {
// The destination is not a valid integer type; see PerformIntConvert.
return SemIR::ErrorInst::ConstantId;
}
auto [is_signed, bit_width_id] = *dest_int_info;
auto width = bit_width_id.has_value()
? context.ints().Get(bit_width_id).getZExtValue()
: arg_val.getBitWidth();
@@ -0,0 +1,57 @@
// 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/full.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/fail_convert_oversized_type.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/fail_convert_oversized_type.carbon
// --- fail_width_not_multiple_of_8.carbon
library "[[@TEST_NAME]]";
// Initializing a binding whose type is an invalid integer type must not crash
// constant evaluation (#7278). The type is diagnosed; the conversion of the
// initializer should then produce an error value, not a CHECK failure.
// CHECK:STDERR: fail_width_not_multiple_of_8.carbon:[[@LINE+11]]:10: error: bit width of integer type literal must be a multiple of 8; use `Core.Int(8388609)` instead [IntWidthNotMultipleOf8]
// CHECK:STDERR: let bad: i8388609 = 0;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_width_not_multiple_of_8.carbon:[[@LINE+7]]:10: error: binding pattern has incomplete type `i8388609` in name binding declaration [IncompleteTypeInBindingDecl]
// CHECK:STDERR: let bad: i8388609 = 0;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: {{.*}}/prelude/types/int.carbon:18:9: note: integer type width of 8388609 is greater than the maximum supported width of 8388608 [IntWidthTooLarge]
// CHECK:STDERR: adapt MakeInt(N);
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
let bad: i8388609 = 0;
// --- fail_width_too_large.carbon
library "[[@TEST_NAME]]";
// CHECK:STDERR: fail_width_too_large.carbon:[[@LINE+7]]:10: error: binding pattern has incomplete type `i16777216` in name binding declaration [IncompleteTypeInBindingDecl]
// CHECK:STDERR: let bad: i16777216 = 0;
// CHECK:STDERR: ^~~~~~~~~
// CHECK:STDERR: {{.*}}/prelude/types/int.carbon:18:9: note: integer type width of 16777216 is greater than the maximum supported width of 8388608 [IntWidthTooLarge]
// CHECK:STDERR: adapt MakeInt(N);
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
let bad: i16777216 = 0;
// --- fail_width_too_large_core_int.carbon
library "[[@TEST_NAME]]";
// CHECK:STDERR: fail_width_too_large_core_int.carbon:[[@LINE+7]]:10: error: binding pattern has incomplete type `i8388609` in name binding declaration [IncompleteTypeInBindingDecl]
// CHECK:STDERR: let bad: Core.Int(8388609) = 0;
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
// CHECK:STDERR: {{.*}}/prelude/types/int.carbon:18:9: note: integer type width of 8388609 is greater than the maximum supported width of 8388608 [IntWidthTooLarge]
// CHECK:STDERR: adapt MakeInt(N);
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
let bad: Core.Int(8388609) = 0;