llvm-project/clang/include/clang/AST/DynamicRecursiveASTVisitor.h
Matheus Izvekov 91cdd35008
[clang] Improve nested name specifier AST representation (#147835)
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 https://github.com/llvm/llvm-project/issues/43179
Fixes https://github.com/llvm/llvm-project/issues/68670
Fixes https://github.com/llvm/llvm-project/issues/92757
2025-08-09 05:06:53 -03:00

306 lines
13 KiB
C++

//===--- DynamicRecursiveASTVisitor.h - Virtual AST Visitor -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the DynamicRecursiveASTVisitor interface, which acts
// identically to RecursiveASTVisitor, except that it uses virtual dispatch
// instead of CRTP, which greatly improves compile times and binary size.
//
// Prefer to use this over RecursiveASTVisitor whenever possible.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_DYNAMIC_RECURSIVE_AST_VISITOR_H
#define LLVM_CLANG_AST_DYNAMIC_RECURSIVE_AST_VISITOR_H
#include "clang/AST/Attr.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/TypeLoc.h"
namespace clang {
class ASTContext;
/// Recursive AST visitor that supports extension via dynamic dispatch.
///
/// Like RecursiveASTVisitor, this class allows for traversal of arbitrarily
/// complex ASTs. The main difference is that this uses virtual functions
/// instead of CRTP, which greatly improves compile times of Clang itself,
/// as well as binary size.
///
/// Instead of functions (e.g. shouldVisitImplicitCode()), this class
/// uses member variables (e.g. ShouldVisitImplicitCode) to control
/// visitation behaviour.
///
/// However, there is no support for overriding some of the less commonly
/// used features of the RAV, such as WalkUpFromX or attribute traversal
/// (attributes can still be traversed, but you can't change what happens
/// when we traverse one).
///
/// The following is a list of RAV features that are NOT customisable:
///
/// - Visiting attributes,
/// - Overriding WalkUpFromX,
/// - Overriding getStmtChildren().
///
/// Furthermore, post-order traversal is not supported at all.
///
/// Prefer to use this over RecursiveASTVisitor unless you absolutely
/// need to use one of the features listed above (e.g. overriding
/// WalkUpFromX or post-order traversal).
///
/// \see RecursiveASTVisitor.
template <bool IsConst> class DynamicRecursiveASTVisitorBase {
protected:
template <typename ASTNode>
using MaybeConst = std::conditional_t<IsConst, const ASTNode, ASTNode>;
public:
/// Whether this visitor should recurse into template instantiations.
bool ShouldVisitTemplateInstantiations = false;
/// Whether this visitor should recurse into the types of TypeLocs.
bool ShouldWalkTypesOfTypeLocs = true;
/// Whether this visitor should recurse into implicit code, e.g.
/// implicit constructors and destructors.
bool ShouldVisitImplicitCode = false;
/// Whether this visitor should recurse into lambda body.
bool ShouldVisitLambdaBody = true;
protected:
DynamicRecursiveASTVisitorBase() = default;
DynamicRecursiveASTVisitorBase(DynamicRecursiveASTVisitorBase &&) = default;
DynamicRecursiveASTVisitorBase(const DynamicRecursiveASTVisitorBase &) =
default;
DynamicRecursiveASTVisitorBase &
operator=(DynamicRecursiveASTVisitorBase &&) = default;
DynamicRecursiveASTVisitorBase &
operator=(const DynamicRecursiveASTVisitorBase &) = default;
public:
virtual void anchor();
virtual ~DynamicRecursiveASTVisitorBase() = default;
/// Recursively visits an entire AST, starting from the TranslationUnitDecl.
/// \returns false if visitation was terminated early.
virtual bool TraverseAST(MaybeConst<ASTContext> &AST);
/// Recursively visit an attribute, by dispatching to
/// Traverse*Attr() based on the argument's dynamic type.
///
/// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is a Null type location).
virtual bool TraverseAttr(MaybeConst<Attr> *At);
/// Recursively visit a constructor initializer. This
/// automatically dispatches to another visitor for the initializer
/// expression, but not for the name of the initializer, so may
/// be overridden for clients that need access to the name.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool
TraverseConstructorInitializer(MaybeConst<CXXCtorInitializer> *Init);
/// Recursively visit a base specifier. This can be overridden by a
/// subclass.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier &Base);
/// Recursively visit a declaration, by dispatching to
/// Traverse*Decl() based on the argument's dynamic type.
///
/// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is NULL).
virtual bool TraverseDecl(MaybeConst<Decl> *D);
/// Recursively visit a name with its location information.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo);
/// Recursively visit a lambda capture. \c Init is the expression that
/// will be used to initialize the capture.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool TraverseLambdaCapture(MaybeConst<LambdaExpr> *LE,
const LambdaCapture *C,
MaybeConst<Expr> *Init);
/// Recursively visit a C++ nested-name-specifier.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool TraverseNestedNameSpecifier(NestedNameSpecifier NNS);
/// Recursively visit a C++ nested-name-specifier with location
/// information.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS);
/// Recursively visit a statement or expression, by
/// dispatching to Traverse*() based on the argument's dynamic type.
///
/// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is nullptr).
virtual bool TraverseStmt(MaybeConst<Stmt> *S);
/// Recursively visit a template argument and dispatch to the
/// appropriate method for the argument type.
///
/// \returns false if the visitation was terminated early, true otherwise.
// FIXME: migrate callers to TemplateArgumentLoc instead.
virtual bool TraverseTemplateArgument(const TemplateArgument &Arg);
/// Recursively visit a template argument location and dispatch to the
/// appropriate method for the argument type.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc);
/// Recursively visit a set of template arguments.
///
/// \returns false if the visitation was terminated early, true otherwise.
// FIXME: take a TemplateArgumentLoc* (or TemplateArgumentListInfo) instead.
// Not virtual for now because no-one overrides it.
bool TraverseTemplateArguments(ArrayRef<TemplateArgument> Args);
/// Recursively visit a template name and dispatch to the
/// appropriate method.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool TraverseTemplateName(TemplateName Template);
/// Recursively visit a type, by dispatching to
/// Traverse*Type() based on the argument's getTypeClass() property.
///
/// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is a Null type).
virtual bool TraverseType(QualType T, bool TraverseQualifier = true);
/// Recursively visit a type with location, by dispatching to
/// Traverse*TypeLoc() based on the argument type's getTypeClass() property.
///
/// \returns false if the visitation was terminated early, true
/// otherwise (including when the argument is a Null type location).
virtual bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true);
/// Recursively visit an Objective-C protocol reference with location
/// information.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc);
/// Traverse a concept (requirement).
virtual bool TraverseTypeConstraint(const TypeConstraint *C);
virtual bool TraverseConceptRequirement(MaybeConst<concepts::Requirement> *R);
virtual bool
TraverseConceptTypeRequirement(MaybeConst<concepts::TypeRequirement> *R);
virtual bool
TraverseConceptExprRequirement(MaybeConst<concepts::ExprRequirement> *R);
virtual bool
TraverseConceptNestedRequirement(MaybeConst<concepts::NestedRequirement> *R);
virtual bool TraverseConceptReference(MaybeConst<ConceptReference> *CR);
virtual bool VisitConceptReference(MaybeConst<ConceptReference> *CR) {
return true;
}
/// Visit a node.
virtual bool VisitAttr(MaybeConst<Attr> *A) { return true; }
virtual bool VisitDecl(MaybeConst<Decl> *D) { return true; }
virtual bool VisitStmt(MaybeConst<Stmt> *S) { return true; }
virtual bool VisitType(MaybeConst<Type> *T) { return true; }
virtual bool VisitTypeLoc(TypeLoc TL) { return true; }
/// Walk up from a node.
bool WalkUpFromDecl(MaybeConst<Decl> *D) { return VisitDecl(D); }
bool WalkUpFromStmt(MaybeConst<Stmt> *S) { return VisitStmt(S); }
bool WalkUpFromType(MaybeConst<Type> *T) { return VisitType(T); }
bool WalkUpFromTypeLoc(TypeLoc TL) { return VisitTypeLoc(TL); }
/// Invoked before visiting a statement or expression via data recursion.
///
/// \returns false to skip visiting the node, true otherwise.
virtual bool dataTraverseStmtPre(MaybeConst<Stmt> *S) { return true; }
/// Invoked after visiting a statement or expression via data recursion.
/// This is not invoked if the previously invoked \c dataTraverseStmtPre
/// returned false.
///
/// \returns false if the visitation was terminated early, true otherwise.
virtual bool dataTraverseStmtPost(MaybeConst<Stmt> *S) { return true; }
virtual bool dataTraverseNode(MaybeConst<Stmt> *S);
#define DEF_TRAVERSE_TMPL_INST(kind) \
virtual bool TraverseTemplateInstantiations( \
MaybeConst<kind##TemplateDecl> *D);
DEF_TRAVERSE_TMPL_INST(Class)
DEF_TRAVERSE_TMPL_INST(Var)
DEF_TRAVERSE_TMPL_INST(Function)
#undef DEF_TRAVERSE_TMPL_INST
// Decls.
#define ABSTRACT_DECL(DECL)
#define DECL(CLASS, BASE) \
bool WalkUpFrom##CLASS##Decl(MaybeConst<CLASS##Decl> *D); \
virtual bool Traverse##CLASS##Decl(MaybeConst<CLASS##Decl> *D);
#include "clang/AST/DeclNodes.inc"
#define DECL(CLASS, BASE) \
virtual bool Visit##CLASS##Decl(MaybeConst<CLASS##Decl> *D) { return true; }
#include "clang/AST/DeclNodes.inc"
// Stmts.
#define ABSTRACT_STMT(STMT)
#define STMT(CLASS, PARENT) virtual bool Traverse##CLASS(MaybeConst<CLASS> *S);
#include "clang/AST/StmtNodes.inc"
#define STMT(CLASS, PARENT) \
bool WalkUpFrom##CLASS(MaybeConst<CLASS> *S); \
virtual bool Visit##CLASS(MaybeConst<CLASS> *S) { return true; }
#include "clang/AST/StmtNodes.inc"
// Types.
#define ABSTRACT_TYPE(CLASS, BASE)
#define TYPE(CLASS, BASE) \
bool WalkUpFrom##CLASS##Type(MaybeConst<CLASS##Type> *T); \
virtual bool Traverse##CLASS##Type(MaybeConst<CLASS##Type> *T, \
bool TraverseQualifier = true);
#include "clang/AST/TypeNodes.inc"
#define TYPE(CLASS, BASE) \
virtual bool Visit##CLASS##Type(MaybeConst<CLASS##Type> *T) { return true; }
#include "clang/AST/TypeNodes.inc"
// TypeLocs.
#define ABSTRACT_TYPELOC(CLASS, BASE)
#define TYPELOC(CLASS, BASE) \
virtual bool Traverse##CLASS##TypeLoc(CLASS##TypeLoc TL, \
bool TraverseQualifier);
#include "clang/AST/TypeLocNodes.def"
#define TYPELOC(CLASS, BASE) \
bool WalkUpFrom##CLASS##TypeLoc(CLASS##TypeLoc TL); \
virtual bool Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { return true; }
#include "clang/AST/TypeLocNodes.def"
};
extern template class DynamicRecursiveASTVisitorBase<false>;
extern template class DynamicRecursiveASTVisitorBase<true>;
using DynamicRecursiveASTVisitor =
DynamicRecursiveASTVisitorBase</*Const=*/false>;
using ConstDynamicRecursiveASTVisitor =
DynamicRecursiveASTVisitorBase</*Const=*/true>;
} // namespace clang
#endif // LLVM_CLANG_AST_DYNAMIC_RECURSIVE_AST_VISITOR_H