Skip to content

Commit 39b9487

Browse files
committed
[clang] AST: fix getAs canonicalization of leaf types
Before this patch, the application of getAs and castAs on a leaf type would always produce a canonical type, which is undesirable because some of these types can be sugared. The user expectation is that getAs only removes top level sugar nodes, leaving all the type sugar on the returned node, but it had an optimization intended for type nodes with no sugar: for these, we can skip the expensive traversal of the top level sugar with a simple canonicalization followed by dyn_cast. The problem is that the concept of leaf type does not map well to what is correct to apply this optimization to. This patch replaces the concept of leaf types with 'always canonical' types, and only applies the canonicalization strategy on them. In order to avoid the performance regression this would cause, as most current users do not care about type sugar, this patch also replaces all of these uses with alternative cast functions which operate through canonicalization. * Introduces castAs variants to complement the getAsTagDecl and derived variants. * Introduces getAsEnumDecl and castAsEnumDecl, complementing the current set, so that all TagDecls are covered. * Introduces getAsCanonical and castAsCanonical, for faster casting when only the canonical type is desired. The getAsTagDecl and related functions are not provided inline, because of the circular dependencies that would involve. So this patch causes a small overall performance regression: <img width="1461" height="18" alt="image" src="https://github.com/user-attachments/assets/061dfb14-9506-4623-91ec-0f02f585d1dd" /> This will be fixed in a later patch, bringing the whole thing back to a positive performance improvement overall: <img width="1462" height="18" alt="image" src="https://github.com/user-attachments/assets/c237e68f-f696-44f4-acc6-a7c7ba5b0976" />
1 parent 63542d5 commit 39b9487

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+206
-195
lines changed

clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
997997
WorkType = QualType{ToBuiltin, FastQualifiersToApply};
998998
}
999999

1000-
const auto *FromEnum = WorkType->getAs<EnumType>();
1000+
const auto *FromEnum = WorkType->getAsCanonical<EnumType>();
10011001
const auto *ToEnum = To->getAs<EnumType>();
10021002
if (FromEnum && ToNumeric && FromEnum->isUnscopedEnumerationType()) {
10031003
// Unscoped enumerations (or enumerations in C) convert to numerics.

clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ AST_MATCHER(ParmVarDecl, isTemplateTypeParameter) {
4545

4646
QualType ParamType =
4747
Node.getType().getNonPackExpansionType()->getPointeeType();
48-
const auto *TemplateType = ParamType->getAs<TemplateTypeParmType>();
48+
const auto *TemplateType = ParamType->getAsCanonical<TemplateTypeParmType>();
4949
if (!TemplateType)
5050
return false;
5151

clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ struct InitializerInsertion {
189189

190190
// Convenience utility to get a RecordDecl from a QualType.
191191
const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
192-
if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
192+
if (const auto *RT = Type->getAsCanonical<RecordType>())
193193
return RT->getOriginalDecl();
194194
return nullptr;
195195
}

clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
131131
return;
132132
}
133133
if (const auto *ECD = dyn_cast<EnumConstantDecl>(Used)) {
134-
if (const auto *ET = ECD->getType()->getAs<EnumType>())
134+
if (const auto *ET = ECD->getType()->getAsCanonical<EnumType>())
135135
removeFromFoundDecls(ET->getOriginalDecl());
136136
}
137137
};

clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ static bool isLockGuardDecl(const NamedDecl *Decl) {
2828
}
2929

3030
static bool isLockGuard(const QualType &Type) {
31-
if (const auto *Record = Type->getAs<RecordType>())
31+
if (const auto *Record = Type->getAsCanonical<RecordType>())
3232
if (const RecordDecl *Decl = Record->getOriginalDecl())
3333
return isLockGuardDecl(Decl);
3434

clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -413,10 +413,11 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
413413

414414
// Arithmetic types are interconvertible, except scoped enums.
415415
if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) {
416-
if ((ParamType->isEnumeralType() &&
417-
ParamType->castAs<EnumType>()->getOriginalDecl()->isScoped()) ||
416+
if ((ParamType->isEnumeralType() && ParamType->castAsCanonical<EnumType>()
417+
->getOriginalDecl()
418+
->isScoped()) ||
418419
(ArgType->isEnumeralType() &&
419-
ArgType->castAs<EnumType>()->getOriginalDecl()->isScoped()))
420+
ArgType->castAsCanonical<EnumType>()->getOriginalDecl()->isScoped()))
420421
return false;
421422

422423
return true;

clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@ bool PopulateSwitch::prepare(const Selection &Sel) {
113113
// Ignore implicit casts, since enums implicitly cast to integer types.
114114
Cond = Cond->IgnoreParenImpCasts();
115115
// Get the canonical type to handle typedefs.
116-
EnumT = Cond->getType().getCanonicalType()->getAsAdjusted<EnumType>();
116+
EnumT = Cond->getType()->getAsCanonical<EnumType>();
117117
if (!EnumT)
118118
return false;
119-
EnumD = EnumT->getOriginalDecl();
120-
if (!EnumD || EnumD->isDependentType())
119+
EnumD = EnumT->getOriginalDecl()->getDefinitionOrSelf();
120+
if (EnumD->isDependentType())
121121
return false;
122122

123123
// Finally, check which cases exist and which are covered.

clang/lib/AST/ASTContext.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2614,10 +2614,10 @@ unsigned ASTContext::getTypeUnadjustedAlign(const Type *T) const {
26142614
return I->second;
26152615

26162616
unsigned UnadjustedAlign;
2617-
if (const auto *RT = T->getAs<RecordType>()) {
2617+
if (const auto *RT = T->getAsCanonical<RecordType>()) {
26182618
const ASTRecordLayout &Layout = getASTRecordLayout(RT->getOriginalDecl());
26192619
UnadjustedAlign = toBits(Layout.getUnadjustedAlignment());
2620-
} else if (const auto *ObjCI = T->getAs<ObjCInterfaceType>()) {
2620+
} else if (const auto *ObjCI = T->getAsCanonical<ObjCInterfaceType>()) {
26212621
const ASTRecordLayout &Layout = getASTObjCInterfaceLayout(ObjCI->getDecl());
26222622
UnadjustedAlign = toBits(Layout.getUnadjustedAlignment());
26232623
} else {
@@ -3552,8 +3552,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
35523552
llvm_unreachable("should never get here");
35533553
}
35543554
case Type::Record: {
3555-
const RecordDecl *RD =
3556-
T->castAs<RecordType>()->getOriginalDecl()->getDefinitionOrSelf();
3555+
const RecordDecl *RD = T->castAsCanonical<RecordType>()->getOriginalDecl();
35573556
const IdentifierInfo *II = RD->getIdentifier();
35583557

35593558
// In C++, an immediate typedef of an anonymous struct or union
@@ -9277,8 +9276,8 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C,
92779276
llvm_unreachable("invalid BuiltinType::Kind value");
92789277
}
92799278

9280-
static char ObjCEncodingForEnumType(const ASTContext *C, const EnumType *ET) {
9281-
EnumDecl *Enum = ET->getOriginalDecl()->getDefinitionOrSelf();
9279+
static char ObjCEncodingForEnumDecl(const ASTContext *C, const EnumDecl *ED) {
9280+
EnumDecl *Enum = ED->getDefinitionOrSelf();
92829281

92839282
// The encoding of an non-fixed enum type is always 'i', regardless of size.
92849283
if (!Enum->isFixed())
@@ -9321,8 +9320,8 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S,
93219320

93229321
S += llvm::utostr(Offset);
93239322

9324-
if (const auto *ET = T->getAs<EnumType>())
9325-
S += ObjCEncodingForEnumType(Ctx, ET);
9323+
if (const auto *ET = T->getAsCanonical<EnumType>())
9324+
S += ObjCEncodingForEnumDecl(Ctx, ET->getOriginalDecl());
93269325
else {
93279326
const auto *BT = T->castAs<BuiltinType>();
93289327
S += getObjCEncodingForPrimitiveType(Ctx, BT);
@@ -9379,7 +9378,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S,
93799378
if (const auto *BT = dyn_cast<BuiltinType>(CT))
93809379
S += getObjCEncodingForPrimitiveType(this, BT);
93819380
else
9382-
S += ObjCEncodingForEnumType(this, cast<EnumType>(CT));
9381+
S += ObjCEncodingForEnumDecl(this, cast<EnumType>(CT)->getOriginalDecl());
93839382
return;
93849383

93859384
case Type::Complex:
@@ -9446,7 +9445,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S,
94469445
S += '*';
94479446
return;
94489447
}
9449-
} else if (const auto *RTy = PointeeTy->getAs<RecordType>()) {
9448+
} else if (const auto *RTy = PointeeTy->getAsCanonical<RecordType>()) {
94509449
const IdentifierInfo *II = RTy->getOriginalDecl()->getIdentifier();
94519450
// GCC binary compat: Need to convert "struct objc_class *" to "#".
94529451
if (II == &Idents.get("objc_class")) {
@@ -11818,10 +11817,10 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
1181811817
if (LHSClass != RHSClass) {
1181911818
// Note that we only have special rules for turning block enum
1182011819
// returns into block int returns, not vice-versa.
11821-
if (const auto *ETy = LHS->getAs<EnumType>()) {
11820+
if (const auto *ETy = LHS->getAsCanonical<EnumType>()) {
1182211821
return mergeEnumWithInteger(*this, ETy, RHS, false);
1182311822
}
11824-
if (const EnumType* ETy = RHS->getAs<EnumType>()) {
11823+
if (const EnumType *ETy = RHS->getAsCanonical<EnumType>()) {
1182511824
return mergeEnumWithInteger(*this, ETy, LHS, BlockReturnType);
1182611825
}
1182711826
// allow block pointer type to match an 'id' type.

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4609,8 +4609,8 @@ UnsignedOrNone Compiler<Emitter>::allocateTemporary(const Expr *E) {
46094609
template <class Emitter>
46104610
const RecordType *Compiler<Emitter>::getRecordTy(QualType Ty) {
46114611
if (const PointerType *PT = dyn_cast<PointerType>(Ty))
4612-
return PT->getPointeeType()->getAs<RecordType>();
4613-
return Ty->getAs<RecordType>();
4612+
return PT->getPointeeType()->getAsCanonical<RecordType>();
4613+
return Ty->getAsCanonical<RecordType>();
46144614
}
46154615

46164616
template <class Emitter> Record *Compiler<Emitter>::getRecord(QualType Ty) {

clang/lib/AST/ByteCode/Pointer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
700700
return true;
701701
}
702702

703-
if (const auto *RT = Ty->getAs<RecordType>()) {
703+
if (const auto *RT = Ty->getAsCanonical<RecordType>()) {
704704
const auto *Record = Ptr.getRecord();
705705
assert(Record && "Missing record descriptor");
706706

0 commit comments

Comments
 (0)