[MLIR] Validate APInt bitwidth in IntegerAttr::get(Type, APInt) (#188725)

IntegerAttr::get(Type, APInt) did not validate that the APInt's bit
width matched the expected bit width for the given type. For integer
types, the APInt width must equal the integer type's width. For index
types, the APInt width must equal IndexType::kInternalStorageBitWidth
(64 bits).

Passing an APInt with the wrong bit width could cause a
non-deterministic crash in StorageUniquer when comparing two IntegerAttr
instances for the same type but with different APInt widths.

This commit adds assertions in the get(Type, APInt) builder to catch
such misuse early in debug builds, providing a clear error message at
the call site rather than a cryptic crash in the storage uniquer.

Fixes #56401

Assisted-by: Claude Code
This commit is contained in:
Mehdi Amini 2026-04-01 10:47:36 +02:00 committed by GitHub
parent 2313989499
commit b1f8c28559
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 50 additions and 0 deletions

View File

@ -720,6 +720,14 @@ def Builtin_IntegerAttr : Builtin_Attr<"Integer", "integer",
"const APInt &":$value), [{
if (type.isSignlessInteger(1))
return BoolAttr::get(type.getContext(), value.getBoolValue());
// Validate that the APInt has the correct bit width for the given type.
if (auto intTy = ::llvm::dyn_cast<IntegerType>(type)) {
assert(value.getBitWidth() == intTy.getWidth() &&
"IntegerAttr::get: APInt bit width must match integer type width");
} else if (::llvm::isa<IndexType>(type)) {
assert(value.getBitWidth() == IndexType::kInternalStorageBitWidth &&
"IntegerAttr::get: APInt bit width must match IndexType internal storage bit width");
}
return $_get(type.getContext(), type, value);
}]>,
AttrBuilder<(ins "const APSInt &":$value), [{

View File

@ -523,4 +523,46 @@ TEST(CopyCountAttr, PrintStripped) {
EXPECT_EQ(str, "|#test.copy_count<hello>|[copy_count<hello>]");
}
//===----------------------------------------------------------------------===//
// IntegerAttr
//===----------------------------------------------------------------------===//
TEST(IntegerAttrTest, CorrectBitWidths) {
MLIRContext context;
// Correct APInt width for i32.
IntegerType i32 = IntegerType::get(&context, 32);
IntegerAttr attr32 = IntegerAttr::get(i32, APInt(32, 42));
EXPECT_EQ(attr32.getType(), i32);
EXPECT_EQ(attr32.getValue().getBitWidth(), 32u);
EXPECT_EQ(attr32.getInt(), 42);
// Correct APInt width for index type.
IndexType indexTy = IndexType::get(&context);
IntegerAttr attrIdx =
IntegerAttr::get(indexTy, APInt(IndexType::kInternalStorageBitWidth, 5));
EXPECT_EQ(attrIdx.getType(), indexTy);
EXPECT_EQ(attrIdx.getValue().getBitWidth(),
(unsigned)IndexType::kInternalStorageBitWidth);
}
#ifndef NDEBUG
TEST(IntegerAttrDeathTest, WrongBitWidthForIntegerType) {
MLIRContext context;
IntegerType i32 = IntegerType::get(&context, 32);
// APInt(8, 1) has bit width 8, but i32 requires 32.
EXPECT_DEATH(IntegerAttr::get(i32, APInt(8, 1)),
"APInt bit width must match integer type width");
}
TEST(IntegerAttrDeathTest, WrongBitWidthForIndexType) {
MLIRContext context;
IndexType indexTy = IndexType::get(&context);
// APInt(1, 1) has bit width 1, but index type requires 64.
EXPECT_DEATH(
IntegerAttr::get(indexTy, APInt(1, 1)),
"APInt bit width must match IndexType internal storage bit width");
}
#endif // NDEBUG
} // namespace