Skip to content

[PATCH 1/7] [clang] Improve nested name specifier AST representation #147835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ ABI Changes in This Version
AST Dumping Potentially Breaking Changes
----------------------------------------

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

Clang Frontend Potentially Breaking Changes
Expand All @@ -95,6 +96,9 @@ Clang Frontend Potentially Breaking Changes

Clang Python Bindings Potentially Breaking Changes
--------------------------------------------------
- TypeKind ``ELABORATED`` is not used anymore, per clang AST changes removing
ElaboratedTypes. The value becomes unused, and all the existing users should
expect the former underlying type to be reported instead.
- ``Cursor.from_location`` now returns ``None`` instead of a null cursor.
This eliminates the last known source of null cursors.
- Almost all ``Cursor`` methods now assert that they are called on non-null cursors.
Expand Down Expand Up @@ -494,6 +498,7 @@ Improvements to Clang's diagnostics
under the subgroup ``-Wunsafe-buffer-usage-in-libc-call``.
- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
``-Wno-error=parentheses``.
- Fix duplicate diagnostics for incomplete type in nested name specifier. (#GH147000)
- Similarly, fold expressions over a comparison operator are now an error by default.
- Clang now better preserves the sugared types of pointers to member.
- Clang now better preserves the presence of the template keyword with dependent
Expand Down Expand Up @@ -987,8 +992,11 @@ Bug Fixes to AST Handling
- Fixed a malformed printout of ``CXXParenListInitExpr`` in certain contexts.
- Fixed a malformed printout of certain calling convention function attributes. (#GH143160)
- Fixed dependency calculation for TypedefTypes (#GH89774)
- Fix incorrect name qualifiers applied to alias CTAD. (#GH136624)
- The ODR checker now correctly hashes the names of conversion operators. (#GH143152)
- Fixed the right parenthesis source location of ``CXXTemporaryObjectExpr``. (#GH143711)
- Fixed ElaboratedTypes appearing within NestedNameSpecifier, which was not a
legal representation. This is fixed because ElaboratedTypes don't exist anymore. (#GH43179) (#GH68670) (#GH92757)
- Fixed a crash when performing an ``IgnoreUnlessSpelledInSource`` traversal of ASTs containing ``catch(...)`` statements. (#GH146103)

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

Expand Down
165 changes: 90 additions & 75 deletions clang/include/clang/AST/ASTContext.h

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ class ASTNodeTraverser
}
void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) {
// FIXME: Provide NestedNamespecifierLoc visitor.
Visit(TL.getQualifierLoc().getTypeLoc());
Visit(TL.getQualifierLoc().castAsTypeLoc());
}
void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) {
Visit(TL.getSizeExpr());
Expand Down Expand Up @@ -772,17 +772,16 @@ class ASTNodeTraverser
}

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

void VisitFriendDecl(const FriendDecl *D) {
if (D->getFriendType()) {
// Traverse any CXXRecordDecl owned by this type, since
// it will not be in the parent context:
if (auto *ET = D->getFriendType()->getType()->getAs<ElaboratedType>())
if (auto *TD = ET->getOwnedTagDecl())
Visit(TD);
if (auto *TT = D->getFriendType()->getType()->getAs<TagType>())
if (TT->isTagOwned())
Visit(TT->getOriginalDecl());
} else {
Visit(D->getFriendDecl());
}
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/ASTTypeTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ class DynTypedNode {

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

/// @{
/// Imposes an order on \c DynTypedNode.
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/AbstractBasicReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class DataStreamBasicReader : public BasicReaderBase<Impl> {
unsigned int_ = asImpl().readUInt32();
Decl *decl = asImpl().template readDeclAs<Decl>();
if (auto *recordDecl = dyn_cast<CXXRecordDecl>(decl))
elemTy = getASTContext().getRecordType(recordDecl);
elemTy = getASTContext().getCanonicalTagType(recordDecl);
else
elemTy = cast<ValueDecl>(decl)->getType();
path.push_back(
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/AbstractBasicWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class DataStreamBasicWriter : public BasicWriterBase<Impl> {
const Decl *baseOrMember = elem.getAsBaseOrMember().getPointer();
if (const auto *recordDecl = dyn_cast<CXXRecordDecl>(baseOrMember)) {
asImpl().writeDeclRef(recordDecl);
elemTy = ctx.getRecordType(recordDecl);
elemTy = ctx.getCanonicalTagType(recordDecl);
} else {
const auto *valueDecl = cast<ValueDecl>(baseOrMember);
asImpl().writeDeclRef(valueDecl);
Expand Down
9 changes: 3 additions & 6 deletions clang/include/clang/AST/CanonicalType.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,21 +551,18 @@ struct CanProxyAdaptor<UnaryTransformType>

template<>
struct CanProxyAdaptor<TagType> : public CanProxyBase<TagType> {
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(TagDecl *, getDecl)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(TagDecl *, getOriginalDecl)
};

template<>
struct CanProxyAdaptor<RecordType> : public CanProxyBase<RecordType> {
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(RecordDecl *, getDecl)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(RecordDecl *, getOriginalDecl)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasConstFields)
};

template<>
struct CanProxyAdaptor<EnumType> : public CanProxyBase<EnumType> {
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(EnumDecl *, getDecl)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isBeingDefined)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(EnumDecl *, getOriginalDecl)
};

template<>
Expand Down
55 changes: 39 additions & 16 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3528,8 +3528,14 @@ class TypeDecl : public NamedDecl {
// check out ASTContext::getTypeDeclType or one of
// ASTContext::getTypedefType, ASTContext::getRecordType, etc. if you
// already know the specific kind of node this is.
const Type *getTypeForDecl() const { return TypeForDecl; }
void setTypeForDecl(const Type *TD) { TypeForDecl = TD; }
const Type *getTypeForDecl() const {
assert(!isa<TagDecl>(this));
return TypeForDecl;
}
void setTypeForDecl(const Type *TD) {
assert(!isa<TagDecl>(this));
TypeForDecl = TD;
}

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

// These types are created lazily, use the ASTContext methods to obtain them.
const Type *getTypeForDecl() const = delete;
void setTypeForDecl(const Type *TD) = delete;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) {
Expand Down Expand Up @@ -3754,14 +3764,6 @@ class TagDecl : public TypeDecl,
/// True if this decl is currently being defined.
void setBeingDefined(bool V = true) { TagDeclBits.IsBeingDefined = V; }

/// Indicates whether it is possible for declarations of this kind
/// to have an out-of-date definition.
///
/// This option is only enabled when modules are enabled.
void setMayHaveOutOfDateDef(bool V = true) {
TagDeclBits.MayHaveOutOfDateDef = V;
}

public:
friend class ASTDeclReader;
friend class ASTDeclWriter;
Expand Down Expand Up @@ -3842,12 +3844,6 @@ class TagDecl : public TypeDecl,
TagDeclBits.IsFreeStanding = isFreeStanding;
}

/// Indicates whether it is possible for declarations of this kind
/// to have an out-of-date definition.
///
/// This option is only enabled when modules are enabled.
bool mayHaveOutOfDateDef() const { return TagDeclBits.MayHaveOutOfDateDef; }

/// Whether this declaration declares a type that is
/// dependent, i.e., a type that somehow depends on template
/// parameters.
Expand Down Expand Up @@ -3888,6 +3884,19 @@ class TagDecl : public TypeDecl,
/// the struct/union/class/enum.
TagDecl *getDefinition() const;

TagDecl *getDefinitionOrSelf() const {
if (TagDecl *Def = getDefinition())
return Def;
return const_cast<TagDecl *>(this);
}

/// Determines whether this entity is in the process of being defined.
bool isEntityBeingDefined() const {
if (const TagDecl *Def = getDefinition())
return Def->isBeingDefined();
return false;
}

StringRef getKindName() const {
return TypeWithKeyword::getTagTypeKindName(getTagKind());
}
Expand Down Expand Up @@ -3958,6 +3967,10 @@ class TagDecl : public TypeDecl,
return getExtInfo()->TemplParamLists[i];
}

// These types are created lazily, use the ASTContext methods to obtain them.
const Type *getTypeForDecl() const = delete;
void setTypeForDecl(const Type *TD) = delete;

using TypeDecl::printName;
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;

Expand Down Expand Up @@ -4087,6 +4100,10 @@ class EnumDecl : public TagDecl {
return cast_or_null<EnumDecl>(TagDecl::getDefinition());
}

EnumDecl *getDefinitionOrSelf() const {
return cast_or_null<EnumDecl>(TagDecl::getDefinitionOrSelf());
}

static EnumDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, EnumDecl *PrevDecl,
Expand Down Expand Up @@ -4469,6 +4486,10 @@ class RecordDecl : public TagDecl {
return cast_or_null<RecordDecl>(TagDecl::getDefinition());
}

RecordDecl *getDefinitionOrSelf() const {
return cast_or_null<RecordDecl>(TagDecl::getDefinitionOrSelf());
}

/// Returns whether this record is a union, or contains (at any nesting level)
/// a union member. This is used by CMSE to warn about possible information
/// leaks.
Expand Down Expand Up @@ -5299,6 +5320,8 @@ void Redeclarable<decl_type>::setPreviousDecl(decl_type *PrevDecl) {
/// We use this function to break a cycle between the inline definitions in
/// Type.h and Decl.h.
inline bool IsEnumDeclComplete(EnumDecl *ED) {
if (const auto *Def = ED->getDefinition())
return Def->isComplete();
return ED->isComplete();
}

Expand Down
10 changes: 0 additions & 10 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,6 @@ class alignas(8) Decl {

virtual ~Decl();

/// Update a potentially out-of-date declaration.
void updateOutOfDate(IdentifierInfo &II) const;

Linkage getCachedLinkage() const {
return static_cast<Linkage>(CacheValidAndLinkage);
}
Expand Down Expand Up @@ -1564,13 +1561,6 @@ class DeclContext {
LLVM_PREFERRED_TYPE(bool)
uint64_t IsFreeStanding : 1;

/// Indicates whether it is possible for declarations of this kind
/// to have an out-of-date definition.
///
/// This option is only enabled when modules are enabled.
LLVM_PREFERRED_TYPE(bool)
uint64_t MayHaveOutOfDateDef : 1;

/// Has the full definition of this type been required by a use somewhere in
/// the TU.
LLVM_PREFERRED_TYPE(bool)
Expand Down
37 changes: 21 additions & 16 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -545,34 +545,25 @@ class CXXRecordDecl : public RecordDecl {
return const_cast<CXXRecordDecl*>(this)->getMostRecentDecl();
}

CXXRecordDecl *getMostRecentNonInjectedDecl() {
CXXRecordDecl *Recent = getMostRecentDecl();
while (Recent->isInjectedClassName()) {
// FIXME: Does injected class name need to be in the redeclarations chain?
assert(Recent->getPreviousDecl());
Recent = Recent->getPreviousDecl();
}
return Recent;
}

const CXXRecordDecl *getMostRecentNonInjectedDecl() const {
return const_cast<CXXRecordDecl*>(this)->getMostRecentNonInjectedDecl();
}

CXXRecordDecl *getDefinition() const {
// We only need an update if we don't already know which
// declaration is the definition.
auto *DD = DefinitionData ? DefinitionData : dataPtr();
return DD ? DD->Definition : nullptr;
}

CXXRecordDecl *getDefinitionOrSelf() const {
if (auto *Def = getDefinition())
return Def;
return const_cast<CXXRecordDecl *>(this);
}

bool hasDefinition() const { return DefinitionData || dataPtr(); }

static CXXRecordDecl *Create(const ASTContext &C, TagKind TK, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id,
CXXRecordDecl *PrevDecl = nullptr,
bool DelayTypeCreation = false);
CXXRecordDecl *PrevDecl = nullptr);
static CXXRecordDecl *CreateLambda(const ASTContext &C, DeclContext *DC,
TypeSourceInfo *Info, SourceLocation Loc,
unsigned DependencyKind, bool IsGeneric,
Expand Down Expand Up @@ -1903,6 +1894,20 @@ class CXXRecordDecl : public RecordDecl {
/// \endcode
bool isInjectedClassName() const;

/// Determines whether this declaration has is canonically of an injected
/// class type. These are non-instantiated class template patterns, which can
/// be used from within the class template itself. For example:
///
/// \code
/// template<class T> struct C {
/// C *t; // Here `C *` is a pointer to an injected class type.
/// };
/// \endcode
bool hasInjectedClassType() const;

CanQualType
getCanonicalTemplateSpecializationType(const ASTContext &Ctx) const;

// Determine whether this type is an Interface Like type for
// __interface inheritance purposes.
bool isInterfaceLike() const;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/DeclObjC.h
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,9 @@ class ObjCTypeParamDecl : public TypedefNameDecl {
/// from the explicitly-specified bound.
SourceLocation getColonLoc() const { return ColonLoc; }

using TypeDecl::getTypeForDecl;
using TypeDecl::setTypeForDecl;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == ObjCTypeParam; }
Expand Down
Loading
Loading