Skip to content

Commit d8acbe8

Browse files
committed
[PATCH 1/6] [clang] Improve nested name specifier AST representation
This is a major change on how we represent nested name qualifications in the AST. * The nested name specifier itself and how it's stored is changed. The prefixes for types are handled within the type hierarchy, which makes canonicalization for them super cheap, no memory allocation required. Also translating a type into nested name specifier form becomes a no-op. An identifier is stored as a DependentNameType. The nested name specifier gains a lightweight handle class, to be used instead of passing around pointers, which is similar to what is implemented for TemplateName. There is still one free bit available, and this handle can be used within a PointerUnion and PointerIntPair, which should keep bit-packing aficionados happy. * The ElaboratedType node is removed, all type nodes in which it could previously apply to can now store the elaborated keyword and name qualifier, tail allocating when present. * TagTypes can now point to the exact declaration found when producing these, as opposed to the previous situation of there only existing one TagType per entity. This increases the amount of type sugar retained, and can have several applications, for example in tracking module ownership, and other tools which care about source file origins, such as IWYU. These TagTypes are lazily allocated, in order to limit the increase in AST size. This patch offers a great performance benefit. It greatly improves compilation time for [stdexec](https://github.com/NVIDIA/stdexec). For one datapoint, for `test_on2.cpp` in that project, which is the slowest compiling test, this patch improves `-c` compilation time by about 7.2%, with the `-fsyntax-only` improvement being at ~12%. This has great results on compile-time-tracker as well: ![image](https://github.com/user-attachments/assets/700dce98-2cab-4aa8-97d1-b038c0bee831) This patch also further enables other optimziations in the future, and will reduce the performance impact of template specialization resugaring when that lands. It has some other miscelaneous drive-by fixes. About the review: Yes the patch is huge, sorry about that. Part of the reason is that I started by the nested name specifier part, before the ElaboratedType part, but that had a huge performance downside, as ElaboratedType is a big performance hog. I didn't have the steam to go back and change the patch after the fact. There is also a lot of internal API changes, and it made sense to remove ElaboratedType in one go, versus removing it from one type at a time, as that would present much more churn to the users. Also, the nested name specifier having a different API avoids missing changes related to how prefixes work now, which could make existing code compile but not work. How to review: The important changes are all in `clang/include/clang/AST` and `clang/lib/AST`, with also important changes in `clang/lib/Sema/TreeTransform.h`. The rest and bulk of the changes are mostly consequences of the changes in API. PS: TagType::getDecl is renamed to `getOriginalDecl` in this patch, just for easier to rebasing. I plan to rename it back after this lands. Fixes #136624 Fixes #147000
1 parent 6422035 commit d8acbe8

Some content is hidden

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

61 files changed

+2437
-2290
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ ABI Changes in This Version
8585
AST Dumping Potentially Breaking Changes
8686
----------------------------------------
8787

88+
- How nested name specifiers are dumped and printed changes, keeping track of clang AST changes.
8889
- Added support for dumping template arguments of structural value kinds.
8990

9091
Clang Frontend Potentially Breaking Changes
@@ -494,6 +495,7 @@ Improvements to Clang's diagnostics
494495
under the subgroup ``-Wunsafe-buffer-usage-in-libc-call``.
495496
- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
496497
``-Wno-error=parentheses``.
498+
- Fix duplicate diagnostics for incomplete type in nested name specifier. (#GH147000)
497499
- Similarly, fold expressions over a comparison operator are now an error by default.
498500
- Clang now better preserves the sugared types of pointers to member.
499501
- Clang now better preserves the presence of the template keyword with dependent
@@ -987,8 +989,11 @@ Bug Fixes to AST Handling
987989
- Fixed a malformed printout of ``CXXParenListInitExpr`` in certain contexts.
988990
- Fixed a malformed printout of certain calling convention function attributes. (#GH143160)
989991
- Fixed dependency calculation for TypedefTypes (#GH89774)
992+
- Fix incorrect name qualifiers applied to alias CTAD. (#GH136624)
990993
- The ODR checker now correctly hashes the names of conversion operators. (#GH143152)
991994
- Fixed the right parenthesis source location of ``CXXTemporaryObjectExpr``. (#GH143711)
995+
- Fixed ElaboratedTypes appearing within NestedNameSpecifier, which was not a
996+
legal representation. This is fixed because ElaboratedTypes don't exist anymore. (#GH43179) (#GH68670) (#GH92757)
992997
- Fixed a crash when performing an ``IgnoreUnlessSpelledInSource`` traversal of ASTs containing ``catch(...)`` statements. (#GH146103)
993998

994999
Miscellaneous Bug Fixes
@@ -1158,6 +1163,8 @@ AST Matchers
11581163
- Ensure ``hasBitWidth`` doesn't crash on bit widths that are dependent on template
11591164
parameters.
11601165
- Ensure ``isDerivedFrom`` matches the correct base in case more than one alias exists.
1166+
- Removed elaboratedType matchers, and related nested name specifier changes,
1167+
following the corresponding changes in the clang AST.
11611168
- Extend ``templateArgumentCountIs`` to support function and variable template
11621169
specialization.
11631170

clang/include/clang/AST/ASTContext.h

Lines changed: 90 additions & 75 deletions
Large diffs are not rendered by default.

clang/include/clang/AST/ASTNodeTraverser.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ class ASTNodeTraverser
510510
}
511511
void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) {
512512
// FIXME: Provide NestedNamespecifierLoc visitor.
513-
Visit(TL.getQualifierLoc().getTypeLoc());
513+
Visit(TL.getQualifierLoc().castAsTypeLoc());
514514
}
515515
void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) {
516516
Visit(TL.getSizeExpr());
@@ -772,17 +772,16 @@ class ASTNodeTraverser
772772
}
773773

774774
void VisitUsingShadowDecl(const UsingShadowDecl *D) {
775-
if (auto *TD = dyn_cast<TypeDecl>(D->getUnderlyingDecl()))
776-
Visit(TD->getTypeForDecl());
775+
Visit(D->getTargetDecl());
777776
}
778777

779778
void VisitFriendDecl(const FriendDecl *D) {
780779
if (D->getFriendType()) {
781780
// Traverse any CXXRecordDecl owned by this type, since
782781
// it will not be in the parent context:
783-
if (auto *ET = D->getFriendType()->getType()->getAs<ElaboratedType>())
784-
if (auto *TD = ET->getOwnedTagDecl())
785-
Visit(TD);
782+
if (auto *TT = D->getFriendType()->getType()->getAs<TagType>())
783+
if (TT->isTagOwned())
784+
Visit(TT->getOriginalDecl());
786785
} else {
787786
Visit(D->getFriendDecl());
788787
}

clang/include/clang/AST/ASTTypeTraits.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ class DynTypedNode {
307307

308308
/// For nodes which represent textual entities in the source code,
309309
/// return their SourceRange. For all other nodes, return SourceRange().
310-
SourceRange getSourceRange() const;
310+
SourceRange getSourceRange(bool IncludeQualifier = false) const;
311311

312312
/// @{
313313
/// Imposes an order on \c DynTypedNode.

clang/include/clang/AST/AbstractBasicReader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class DataStreamBasicReader : public BasicReaderBase<Impl> {
197197
unsigned int_ = asImpl().readUInt32();
198198
Decl *decl = asImpl().template readDeclAs<Decl>();
199199
if (auto *recordDecl = dyn_cast<CXXRecordDecl>(decl))
200-
elemTy = getASTContext().getRecordType(recordDecl);
200+
elemTy = getASTContext().getCanonicalTagType(recordDecl);
201201
else
202202
elemTy = cast<ValueDecl>(decl)->getType();
203203
path.push_back(

clang/include/clang/AST/AbstractBasicWriter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ class DataStreamBasicWriter : public BasicWriterBase<Impl> {
181181
const Decl *baseOrMember = elem.getAsBaseOrMember().getPointer();
182182
if (const auto *recordDecl = dyn_cast<CXXRecordDecl>(baseOrMember)) {
183183
asImpl().writeDeclRef(recordDecl);
184-
elemTy = ctx.getRecordType(recordDecl);
184+
elemTy = ctx.getCanonicalTagType(recordDecl);
185185
} else {
186186
const auto *valueDecl = cast<ValueDecl>(baseOrMember);
187187
asImpl().writeDeclRef(valueDecl);

clang/include/clang/AST/CanonicalType.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -551,21 +551,18 @@ struct CanProxyAdaptor<UnaryTransformType>
551551

552552
template<>
553553
struct CanProxyAdaptor<TagType> : public CanProxyBase<TagType> {
554-
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(TagDecl *, getDecl)
555-
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
554+
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(TagDecl *, getOriginalDecl)
556555
};
557556

558557
template<>
559558
struct CanProxyAdaptor<RecordType> : public CanProxyBase<RecordType> {
560-
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(RecordDecl *, getDecl)
561-
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
559+
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(RecordDecl *, getOriginalDecl)
562560
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasConstFields)
563561
};
564562

565563
template<>
566564
struct CanProxyAdaptor<EnumType> : public CanProxyBase<EnumType> {
567-
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(EnumDecl *, getDecl)
568-
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
565+
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(EnumDecl *, getOriginalDecl)
569566
};
570567

571568
template<>

clang/include/clang/AST/Decl.h

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3528,8 +3528,14 @@ class TypeDecl : public NamedDecl {
35283528
// check out ASTContext::getTypeDeclType or one of
35293529
// ASTContext::getTypedefType, ASTContext::getRecordType, etc. if you
35303530
// already know the specific kind of node this is.
3531-
const Type *getTypeForDecl() const { return TypeForDecl; }
3532-
void setTypeForDecl(const Type *TD) { TypeForDecl = TD; }
3531+
const Type *getTypeForDecl() const {
3532+
assert(!isa<TagDecl>(this));
3533+
return TypeForDecl;
3534+
}
3535+
void setTypeForDecl(const Type *TD) {
3536+
assert(!isa<TagDecl>(this));
3537+
TypeForDecl = TD;
3538+
}
35333539

35343540
SourceLocation getBeginLoc() const LLVM_READONLY { return LocStart; }
35353541
void setLocStart(SourceLocation L) { LocStart = L; }
@@ -3635,6 +3641,10 @@ class TypedefNameDecl : public TypeDecl, public Redeclarable<TypedefNameDecl> {
36353641
return isTransparentTagSlow();
36363642
}
36373643

3644+
// These types are created lazily, use the ASTContext methods to obtain them.
3645+
const Type *getTypeForDecl() const = delete;
3646+
void setTypeForDecl(const Type *TD) = delete;
3647+
36383648
// Implement isa/cast/dyncast/etc.
36393649
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
36403650
static bool classofKind(Kind K) {
@@ -3754,14 +3764,6 @@ class TagDecl : public TypeDecl,
37543764
/// True if this decl is currently being defined.
37553765
void setBeingDefined(bool V = true) { TagDeclBits.IsBeingDefined = V; }
37563766

3757-
/// Indicates whether it is possible for declarations of this kind
3758-
/// to have an out-of-date definition.
3759-
///
3760-
/// This option is only enabled when modules are enabled.
3761-
void setMayHaveOutOfDateDef(bool V = true) {
3762-
TagDeclBits.MayHaveOutOfDateDef = V;
3763-
}
3764-
37653767
public:
37663768
friend class ASTDeclReader;
37673769
friend class ASTDeclWriter;
@@ -3842,12 +3844,6 @@ class TagDecl : public TypeDecl,
38423844
TagDeclBits.IsFreeStanding = isFreeStanding;
38433845
}
38443846

3845-
/// Indicates whether it is possible for declarations of this kind
3846-
/// to have an out-of-date definition.
3847-
///
3848-
/// This option is only enabled when modules are enabled.
3849-
bool mayHaveOutOfDateDef() const { return TagDeclBits.MayHaveOutOfDateDef; }
3850-
38513847
/// Whether this declaration declares a type that is
38523848
/// dependent, i.e., a type that somehow depends on template
38533849
/// parameters.
@@ -3888,6 +3884,19 @@ class TagDecl : public TypeDecl,
38883884
/// the struct/union/class/enum.
38893885
TagDecl *getDefinition() const;
38903886

3887+
TagDecl *getDefinitionOrSelf() const {
3888+
if (TagDecl *Def = getDefinition())
3889+
return Def;
3890+
return const_cast<TagDecl *>(this);
3891+
}
3892+
3893+
/// Determines whether this entity is in the process of being defined.
3894+
bool isEntityBeingDefined() const {
3895+
if (const TagDecl *Def = getDefinition())
3896+
return Def->isBeingDefined();
3897+
return false;
3898+
}
3899+
38913900
StringRef getKindName() const {
38923901
return TypeWithKeyword::getTagTypeKindName(getTagKind());
38933902
}
@@ -3958,6 +3967,10 @@ class TagDecl : public TypeDecl,
39583967
return getExtInfo()->TemplParamLists[i];
39593968
}
39603969

3970+
// These types are created lazily, use the ASTContext methods to obtain them.
3971+
const Type *getTypeForDecl() const = delete;
3972+
void setTypeForDecl(const Type *TD) = delete;
3973+
39613974
using TypeDecl::printName;
39623975
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
39633976

@@ -4087,6 +4100,10 @@ class EnumDecl : public TagDecl {
40874100
return cast_or_null<EnumDecl>(TagDecl::getDefinition());
40884101
}
40894102

4103+
EnumDecl *getDefinitionOrSelf() const {
4104+
return cast_or_null<EnumDecl>(TagDecl::getDefinitionOrSelf());
4105+
}
4106+
40904107
static EnumDecl *Create(ASTContext &C, DeclContext *DC,
40914108
SourceLocation StartLoc, SourceLocation IdLoc,
40924109
IdentifierInfo *Id, EnumDecl *PrevDecl,
@@ -4469,6 +4486,10 @@ class RecordDecl : public TagDecl {
44694486
return cast_or_null<RecordDecl>(TagDecl::getDefinition());
44704487
}
44714488

4489+
RecordDecl *getDefinitionOrSelf() const {
4490+
return cast_or_null<RecordDecl>(TagDecl::getDefinitionOrSelf());
4491+
}
4492+
44724493
/// Returns whether this record is a union, or contains (at any nesting level)
44734494
/// a union member. This is used by CMSE to warn about possible information
44744495
/// leaks.
@@ -5299,6 +5320,8 @@ void Redeclarable<decl_type>::setPreviousDecl(decl_type *PrevDecl) {
52995320
/// We use this function to break a cycle between the inline definitions in
53005321
/// Type.h and Decl.h.
53015322
inline bool IsEnumDeclComplete(EnumDecl *ED) {
5323+
if (const auto *Def = ED->getDefinition())
5324+
return Def->isComplete();
53025325
return ED->isComplete();
53035326
}
53045327

clang/include/clang/AST/DeclBase.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,6 @@ class alignas(8) Decl {
410410

411411
virtual ~Decl();
412412

413-
/// Update a potentially out-of-date declaration.
414-
void updateOutOfDate(IdentifierInfo &II) const;
415-
416413
Linkage getCachedLinkage() const {
417414
return static_cast<Linkage>(CacheValidAndLinkage);
418415
}
@@ -1564,13 +1561,6 @@ class DeclContext {
15641561
LLVM_PREFERRED_TYPE(bool)
15651562
uint64_t IsFreeStanding : 1;
15661563

1567-
/// Indicates whether it is possible for declarations of this kind
1568-
/// to have an out-of-date definition.
1569-
///
1570-
/// This option is only enabled when modules are enabled.
1571-
LLVM_PREFERRED_TYPE(bool)
1572-
uint64_t MayHaveOutOfDateDef : 1;
1573-
15741564
/// Has the full definition of this type been required by a use somewhere in
15751565
/// the TU.
15761566
LLVM_PREFERRED_TYPE(bool)

clang/include/clang/AST/DeclCXX.h

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -545,34 +545,25 @@ class CXXRecordDecl : public RecordDecl {
545545
return const_cast<CXXRecordDecl*>(this)->getMostRecentDecl();
546546
}
547547

548-
CXXRecordDecl *getMostRecentNonInjectedDecl() {
549-
CXXRecordDecl *Recent = getMostRecentDecl();
550-
while (Recent->isInjectedClassName()) {
551-
// FIXME: Does injected class name need to be in the redeclarations chain?
552-
assert(Recent->getPreviousDecl());
553-
Recent = Recent->getPreviousDecl();
554-
}
555-
return Recent;
556-
}
557-
558-
const CXXRecordDecl *getMostRecentNonInjectedDecl() const {
559-
return const_cast<CXXRecordDecl*>(this)->getMostRecentNonInjectedDecl();
560-
}
561-
562548
CXXRecordDecl *getDefinition() const {
563549
// We only need an update if we don't already know which
564550
// declaration is the definition.
565551
auto *DD = DefinitionData ? DefinitionData : dataPtr();
566552
return DD ? DD->Definition : nullptr;
567553
}
568554

555+
CXXRecordDecl *getDefinitionOrSelf() const {
556+
if (auto *Def = getDefinition())
557+
return Def;
558+
return const_cast<CXXRecordDecl *>(this);
559+
}
560+
569561
bool hasDefinition() const { return DefinitionData || dataPtr(); }
570562

571563
static CXXRecordDecl *Create(const ASTContext &C, TagKind TK, DeclContext *DC,
572564
SourceLocation StartLoc, SourceLocation IdLoc,
573565
IdentifierInfo *Id,
574-
CXXRecordDecl *PrevDecl = nullptr,
575-
bool DelayTypeCreation = false);
566+
CXXRecordDecl *PrevDecl = nullptr);
576567
static CXXRecordDecl *CreateLambda(const ASTContext &C, DeclContext *DC,
577568
TypeSourceInfo *Info, SourceLocation Loc,
578569
unsigned DependencyKind, bool IsGeneric,
@@ -1903,6 +1894,20 @@ class CXXRecordDecl : public RecordDecl {
19031894
/// \endcode
19041895
bool isInjectedClassName() const;
19051896

1897+
/// Determines whether this declaration has is canonically of an injected
1898+
/// class type. These are non-instantiated class template patterns, which can
1899+
/// be used from within the class template itself. For example:
1900+
///
1901+
/// \code
1902+
/// template<class T> struct C {
1903+
/// C *t; // Here `C *` is a pointer to an injected class type.
1904+
/// };
1905+
/// \endcode
1906+
bool hasInjectedClassType() const;
1907+
1908+
CanQualType
1909+
getCanonicalTemplateSpecializationType(const ASTContext &Ctx) const;
1910+
19061911
// Determine whether this type is an Interface Like type for
19071912
// __interface inheritance purposes.
19081913
bool isInterfaceLike() const;

0 commit comments

Comments
 (0)