Skip to content

Commit 8cba03c

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 6560866 commit 8cba03c

File tree

161 files changed

+1188
-1925
lines changed

Some content is hidden

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

161 files changed

+1188
-1925
lines changed

clang-tools-extra/clang-doc/Serialize.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -901,9 +901,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
901901
if (!D->isThisDeclarationADefinition())
902902
return;
903903
for (const CXXBaseSpecifier &B : D->bases()) {
904-
if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
905-
if (const CXXRecordDecl *Base = cast_or_null<CXXRecordDecl>(
906-
Ty->getOriginalDecl()->getDefinition())) {
904+
if (const auto *Base = B.getType()->getAsCXXRecordDecl()) {
905+
if (Base->isCompleteDefinition()) {
907906
// Initialized without USR and name, this will be set in the following
908907
// if-else stmt.
909908
BaseRecordInfo BI(

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/bugprone/TaggedUnionMemberCountCheck.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,15 +169,8 @@ void TaggedUnionMemberCountCheck::check(
169169
if (!Root || !UnionField || !TagField)
170170
return;
171171

172-
const auto *UnionDef =
173-
UnionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
174-
const auto *EnumDef = llvm::dyn_cast<EnumDecl>(
175-
TagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
176-
177-
assert(UnionDef && "UnionDef is missing!");
178-
assert(EnumDef && "EnumDef is missing!");
179-
if (!UnionDef || !EnumDef)
180-
return;
172+
const auto *UnionDef = UnionField->getType()->castAsRecordDecl();
173+
const auto *EnumDef = TagField->getType()->castAsEnumDecl();
181174

182175
const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
183176
auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef);

clang-tools-extra/clang-tidy/cert/DefaultOperatorNewAlignmentCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ void DefaultOperatorNewAlignmentCheck::check(
3131
return;
3232
const TagDecl *D = T->getAsTagDecl();
3333
// Alignment can not be obtained for undefined type.
34-
if (!D || !D->getDefinition() || !D->isCompleteDefinition())
34+
if (!D || !D->isCompleteDefinition())
3535
return;
3636

3737
ASTContext &Context = D->getASTContext();

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/cppcoreguidelines/SlicingCheck.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,8 @@ void SlicingCheck::diagnoseSlicedOverriddenMethods(
9090
}
9191
// Recursively process bases.
9292
for (const auto &Base : DerivedDecl.bases()) {
93-
if (const auto *BaseRecordType = Base.getType()->getAs<RecordType>()) {
94-
if (const auto *BaseRecord = cast_or_null<CXXRecordDecl>(
95-
BaseRecordType->getOriginalDecl()->getDefinition()))
93+
if (const auto *BaseRecord = Base.getType()->getAsCXXRecordDecl()) {
94+
if (BaseRecord->isCompleteDefinition())
9695
diagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl);
9796
}
9897
}

clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,10 @@ bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
7171
for (const auto &I : Node->bases()) {
7272
if (I.isVirtual())
7373
continue;
74-
const auto *Ty = I.getType()->getAs<RecordType>();
75-
if (!Ty)
74+
const auto *Base = I.getType()->getAsCXXRecordDecl();
75+
if (!Base)
7676
continue;
77-
const RecordDecl *D = Ty->getOriginalDecl()->getDefinition();
78-
if (!D)
79-
continue;
80-
const auto *Base = cast<CXXRecordDecl>(D);
77+
assert(Base->isCompleteDefinition());
8178
if (!isInterface(Base)) {
8279
addNodeToInterfaceMap(Node, false);
8380
return false;
@@ -103,23 +100,21 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
103100
for (const auto &I : D->bases()) {
104101
if (I.isVirtual())
105102
continue;
106-
const auto *Ty = I.getType()->getAs<RecordType>();
107-
if (!Ty)
103+
const auto *Base = I.getType()->getAsCXXRecordDecl();
104+
if (!Base)
108105
continue;
109-
const auto *Base =
110-
cast<CXXRecordDecl>(Ty->getOriginalDecl()->getDefinition());
106+
assert(Base->isCompleteDefinition());
111107
if (!isInterface(Base))
112108
NumConcrete++;
113109
}
114110

115111
// Check virtual bases to see if there is more than one concrete
116112
// non-virtual base.
117113
for (const auto &V : D->vbases()) {
118-
const auto *Ty = V.getType()->getAs<RecordType>();
119-
if (!Ty)
114+
const auto *Base = V.getType()->getAsCXXRecordDecl();
115+
if (!Base)
120116
continue;
121-
const auto *Base =
122-
cast<CXXRecordDecl>(Ty->getOriginalDecl()->getDefinition());
117+
assert(Base->isCompleteDefinition());
123118
if (!isInterface(Base))
124119
NumConcrete++;
125120
}

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

0 commit comments

Comments
 (0)