Canonicalize different spellings of the same integer generic parameter.

`Foo<256>`, `Foo<2_56>`, and `Foo<0x100>` are all canonically the same type.
Fixes rdar://144736386.
This commit is contained in:
Joe Groff
2025-04-03 18:56:00 -07:00
parent 41f7ac3e75
commit 5a09c0b4e0
6 changed files with 52 additions and 4 deletions

View File

@@ -207,7 +207,7 @@ TYPE(PackExpansion, Type)
TYPE(PackElement, Type)
UNCHECKED_TYPE(TypeVariable, Type)
UNCHECKED_TYPE(ErrorUnion, Type)
ALWAYS_CANONICAL_TYPE(Integer, Type)
TYPE(Integer, Type)
ABSTRACT_SUGARED_TYPE(Sugar, Type)
SUGARED_TYPE(TypeAlias, SugarType)
SUGARED_TYPE(Locatable, SugarType)

View File

@@ -7884,10 +7884,27 @@ class IntegerType final : public TypeBase, public llvm::FoldingSetNode {
friend class ASTContext;
StringRef Value;
// Integers may not be canonical, but don't have any structural type
// components from which to get the ASTContext, so we need to store a
// reference to it ourselves.
const ASTContext &Context;
static const ASTContext *
getCanonicalIntegerLiteralContext(StringRef value, const ASTContext &ctx) {
for (char c : value) {
// A canonical integer literal consists only of ASCII decimal digits.
if (c < '0' || c > '9') {
return nullptr;
}
}
return &ctx;
}
IntegerType(StringRef value, bool isNegative, const ASTContext &ctx) :
TypeBase(TypeKind::Integer, &ctx, RecursiveTypeProperties()),
Value(value) {
TypeBase(TypeKind::Integer, getCanonicalIntegerLiteralContext(value, ctx),
RecursiveTypeProperties()),
Value(value),
Context(ctx) {
Bits.IntegerType.IsNegative = isNegative;
}
@@ -7917,6 +7934,8 @@ public:
static bool classof(const TypeBase *T) {
return T->getKind() == TypeKind::Integer;
}
const ASTContext &getASTContext() { return Context; }
};
DEFINE_EMPTY_CAN_TYPE_WRAPPER(IntegerType, Type)

View File

@@ -3724,8 +3724,9 @@ IntegerType *IntegerType::get(StringRef value, bool isNegative,
IntegerType::Profile(id, value, isNegative);
void *insertPos;
if (auto intType = ctx.getImpl().IntegerTypes.FindNodeOrInsertPos(id, insertPos))
if (auto intType = ctx.getImpl().IntegerTypes.FindNodeOrInsertPos(id, insertPos)) {
return intType;
}
auto strCopy = ctx.AllocateCopy(value);

View File

@@ -6532,6 +6532,7 @@ namespace {
printCommon("integer_type", label);
printFlag(T->isNegative(), "is_negative");
printFieldQuoted(T->getValue(), Label::always("value"), LiteralValueColor);
printFieldQuoted(T->getDigitsText(), Label::always("text"), IdentifierColor);
printFoot();
}

View File

@@ -1878,6 +1878,18 @@ CanType TypeBase::computeCanonicalType() {
Result = ErrorUnionType::get(ctx, newTerms).getPointer();
break;
}
case TypeKind::Integer: {
auto intTy = cast<IntegerType>(this);
APInt value = intTy->getValue();
if (intTy->isNegative()) {
value = -value;
}
SmallString<20> canonicalText;
value.toStringUnsigned(canonicalText);
Result = IntegerType::get(canonicalText, intTy->isNegative(),
intTy->getASTContext());
break;
}
}
// Cache the canonical type for future queries.

View File

@@ -0,0 +1,15 @@
// RUN: %target-swift-frontend -disable-availability-checking -typecheck -verify %s
struct Foo<let n: Int> {}
func foo(x: Foo<256>) {}
func bar(x: Foo<0x100>) {
foo(x: x)
}
func oof(x: Foo<-256>) {}
func rab(x: Foo<-0x100>) {
oof(x: x)
}