Skip to content

Enable AST mutation in the constant evaluator #115168

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

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 6 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
25 changes: 25 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,17 @@ struct TypeInfoChars {
}
};

// Interface that allows constant evaluator to mutate AST.
// Sema is the only entity that can implement this.
struct EvalASTMutator {
virtual ~EvalASTMutator() = default;

virtual void
InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
FunctionDecl *Function, bool Recursive,
bool DefinitionRequired, bool AtEndOfTU) = 0;
};

/// Holds long-lived AST nodes (such as types and decls) that can be
/// referred to throughout the semantic analysis of a file.
class ASTContext : public RefCountedBase<ASTContext> {
Expand Down Expand Up @@ -671,7 +682,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// Keeps track of the deallocated DeclListNodes for future reuse.
DeclListNode *ListNodeFreeList = nullptr;

/// Implementation of the interface that Sema provides during its
/// construction.
EvalASTMutator *ASTMutator = nullptr;

public:
/// Returns an object that is capable of modifying AST,
/// or nullptr if it's not available. The latter happens when
/// Sema is not available.
EvalASTMutator *getASTMutator() const { return ASTMutator; }

IdentifierTable &Idents;
SelectorTable &Selectors;
Builtin::Context &BuiltinInfo;
Expand Down Expand Up @@ -3509,6 +3529,11 @@ OPT_LIST(V)

void ReleaseDeclContextMaps();

/// This is a function that is implemented in the Sema layer,
/// that needs friendship to initialize ASTMutator without this capability
/// being available in the public interface of ASTContext.
friend void injectASTMutatorIntoASTContext(ASTContext &, EvalASTMutator *);

public:
enum PragmaSectionFlag : unsigned {
PSF_None = 0,
Expand Down
18 changes: 18 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ enum class OverloadCandidateParamOrder : char;
enum OverloadCandidateRewriteKind : unsigned;
class OverloadCandidateSet;
class Preprocessor;
class Sema;
class SemaAMDGPU;
class SemaARM;
class SemaAVR;
Expand Down Expand Up @@ -352,6 +353,16 @@ struct SkipBodyInfo {
NamedDecl *New = nullptr;
};

/// Implementation of EvalASTMutator interface that enables constant evaluator
/// to modify AST, e.g. to instantiate templates.
struct SemaASTMutator : EvalASTMutator {
Sema &SemaRef;
SemaASTMutator(Sema &SemaRef);
void InstantiateFunctionDefinition(
SourceLocation PointOfInstantiation, FunctionDecl *Function,
bool Recursive, bool DefinitionRequired, bool AtEndOfTU) override;
};

/// Describes the result of template argument deduction.
///
/// The TemplateDeductionResult enumeration describes the result of
Expand Down Expand Up @@ -1042,6 +1053,9 @@ class Sema final : public SemaBase {
/// CurContext - This is the current declaration context of parsing.
DeclContext *CurContext;

/// Get a Sema implementation of EvalASTMutator interface.
SemaASTMutator *getASTMutator() { return &ASTMutator; }

SemaAMDGPU &AMDGPU() {
assert(AMDGPUPtr);
return *AMDGPUPtr;
Expand Down Expand Up @@ -1199,6 +1213,10 @@ class Sema final : public SemaBase {

mutable IdentifierInfo *Ident_super;

/// EvalASTMutator implementation that can be passed to constant evaluator
/// to enable it to do AST mutations, e.g. template instantiation.
SemaASTMutator ASTMutator;

std::unique_ptr<SemaAMDGPU> AMDGPUPtr;
std::unique_ptr<SemaARM> ARMPtr;
std::unique_ptr<SemaAVR> AVRPtr;
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,7 @@ namespace {
}

ASTContext &getASTContext() const override { return Ctx; }
EvalASTMutator *getASTMutator() const { return Ctx.getASTMutator(); }

void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
Expand Down Expand Up @@ -8328,6 +8329,13 @@ class ExprEvaluatorBase

const FunctionDecl *Definition = nullptr;
Stmt *Body = FD->getBody(Definition);
if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getASTMutator() &&
!Definition && FD->getTemplateInstantiationPattern()) {
Info.getASTMutator()->InstantiateFunctionDefinition(
E->getExprLoc(), const_cast<FunctionDecl *>(FD),
/*Recursive=*/true, /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);
Body = FD->getBody(Definition);
}

if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body) ||
!HandleFunctionCall(E->getExprLoc(), Definition, This, E, Args, Call,
Expand Down
21 changes: 20 additions & 1 deletion clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ class SemaPPCallbacks : public PPCallbacks {
} // end namespace sema
} // end namespace clang

void clang::injectASTMutatorIntoASTContext(ASTContext &Context,
EvalASTMutator *ASTMutator) {
Context.ASTMutator = ASTMutator;
}

SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {}

const unsigned Sema::MaxAlignmentExponent;
const uint64_t Sema::MaximumAlignment;

Expand All @@ -221,7 +228,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr),
OpaqueParser(nullptr), CurContext(nullptr), ExternalSource(nullptr),
StackHandler(Diags), CurScope(nullptr), Ident_super(nullptr),
AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
ASTMutator(*this), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
ARMPtr(std::make_unique<SemaARM>(*this)),
AVRPtr(std::make_unique<SemaAVR>(*this)),
BPFPtr(std::make_unique<SemaBPF>(*this)),
Expand Down Expand Up @@ -298,6 +305,11 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
SemaPPCallbackHandler->set(*this);

CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod());

/// Initialize ASTMutator within ASTContext.
/// This is very intentionally not a part of public interface
/// of ASTContext.
injectASTMutatorIntoASTContext(Context, getASTMutator());
}

// Anchor Sema's type info to this TU.
Expand Down Expand Up @@ -2798,3 +2810,10 @@ Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) {

return CreateAnnotationAttr(AL, Str, Args);
}

void SemaASTMutator::InstantiateFunctionDefinition(
SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive,
bool DefinitionRequired, bool AtEndOfTU) {
SemaRef.InstantiateFunctionDefinition(
PointOfInstantiation, Function, Recursive, DefinitionRequired, AtEndOfTU);
}
37 changes: 37 additions & 0 deletions clang/test/SemaCXX/constexpr-function-instantiation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20-23 %s
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=cxx20-23 %s
// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=cxx26 %s

// cxx26-no-diagnostics

namespace GH73232 {
namespace ex1 {
template <typename T>
constexpr void g(T); // #ex1-g-decl

constexpr int f() {
g(0); // #ex1-g-call
return 0;
}

template <typename T>
constexpr void g(T) {}

constexpr auto z = f(); // #ex1-z-defn
// cxx20-23-error@-1 {{constexpr variable 'z' must be initialized by a constant expression}}
// cxx20-23-note@#ex1-g-call {{undefined function 'g<int>' cannot be used in a constant expression}}
// cxx20-23-note@#ex1-z-defn {{in call to 'f()'}}
// cxx20-23-note@#ex1-g-decl {{declared here}}
} // namespace ex1

namespace ex2 {
template <typename> constexpr static void fromType();

void registerConverter() { fromType<int>(); }
template <typename> struct QMetaTypeId {};
template <typename T> constexpr void fromType() {
(void)QMetaTypeId<T>{};
} // #1
template <> struct QMetaTypeId<int> {}; // #20428
} // namespace ex2
} // namespace GH73232
Loading