https://github.com/Endilll updated https://github.com/llvm/llvm-project/pull/115168
>From 5ca48e03412b1b8e9253f13356b9cc957f6fd9e5 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Wed, 6 Nov 2024 17:58:43 +0300 Subject: [PATCH 1/8] Add EvalASTMutator interface with `InstantiateFunctionDefinition` function --- clang/include/clang/AST/ASTContext.h | 2 +- clang/include/clang/AST/Decl.h | 22 ++++++++++--- clang/include/clang/AST/Expr.h | 3 +- clang/include/clang/Sema/Sema.h | 17 ++++++++++ clang/lib/AST/ASTContext.cpp | 4 +-- clang/lib/AST/Decl.cpp | 16 +++++----- clang/lib/AST/ExprConstant.cpp | 24 +++++++++++--- clang/lib/Sema/Sema.cpp | 11 ++++++- clang/lib/Sema/SemaDecl.cpp | 5 +-- .../constexpr-function-instantiation.cpp | 31 +++++++++++++++++++ 10 files changed, 113 insertions(+), 22 deletions(-) create mode 100644 clang/test/SemaCXX/constexpr-function-instantiation.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index a4d36f2eacd5d1..d143591de9f2cd 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -3258,7 +3258,7 @@ class ASTContext : public RefCountedBase<ASTContext> { /// /// \returns true if the function/var must be CodeGen'ed/deserialized even if /// it is not used. - bool DeclMustBeEmitted(const Decl *D); + bool DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator = nullptr); /// Visits all versions of a multiversioned function with the passed /// predicate. diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 7ff35d73df5997..89a2833c82194d 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -78,6 +78,18 @@ class UnresolvedSetImpl; class VarTemplateDecl; enum class ImplicitParamKind; +/// Interface that allows constant evaluator to mutate AST. +/// When constant evaluation is triggered by Sema, it can supply a proper +/// implementation of this interface. +struct EvalASTMutator { + virtual ~EvalASTMutator() = default; + + virtual void + InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function, bool Recursive, + bool DefinitionRequired, bool AtEndOfTU) = 0; +}; + /// The top declaration context. class TranslationUnitDecl : public Decl, public DeclContext, @@ -1355,11 +1367,12 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// Attempt to evaluate the value of the initializer attached to this /// declaration, and produce notes explaining why it cannot be evaluated. /// Returns a pointer to the value if evaluation succeeded, 0 otherwise. - APValue *evaluateValue() const; + APValue *evaluateValue(EvalASTMutator *ASTMutator = nullptr) const; private: APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes, - bool IsConstantInitialization) const; + bool IsConstantInitialization, + EvalASTMutator *ASTMutator = nullptr) const; public: /// Return the already-evaluated value of this variable's @@ -1391,8 +1404,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// Evaluate the initializer of this variable to determine whether it's a /// constant initializer. Should only be called once, after completing the /// definition of the variable. - bool checkForConstantInitialization( - SmallVectorImpl<PartialDiagnosticAt> &Notes) const; + bool + checkForConstantInitialization(SmallVectorImpl<PartialDiagnosticAt> &Notes, + EvalASTMutator *ASTMutator = nullptr) const; void setInitStyle(InitializationStyle Style) { VarDeclBits.InitStyle = Style; diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 466c65a9685ad3..ccf7fd226b5a8f 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -734,7 +734,8 @@ class Expr : public ValueStmt { bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx, const VarDecl *VD, SmallVectorImpl<PartialDiagnosticAt> &Notes, - bool IsConstantInitializer) const; + bool IsConstantInitializer, + EvalASTMutator *ASTMutator = nullptr) const; /// EvaluateWithSubstitution - Evaluate an expression as if from the context /// of a call to the given function with the given arguments, inside an diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 93d98e1cbb9c81..1c0c6949573335 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -153,6 +153,7 @@ enum class OverloadCandidateParamOrder : char; enum OverloadCandidateRewriteKind : unsigned; class OverloadCandidateSet; class Preprocessor; +class Sema; class SemaAMDGPU; class SemaARM; class SemaAVR; @@ -352,6 +353,15 @@ 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 @@ -1042,6 +1052,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; @@ -1199,6 +1212,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; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 69892bda42b256..0aaf23af28d3d6 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12522,7 +12522,7 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) const { basicGVALinkageForVariable(*this, VD))); } -bool ASTContext::DeclMustBeEmitted(const Decl *D) { +bool ASTContext::DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator) { if (const auto *VD = dyn_cast<VarDecl>(D)) { if (!VD->isFileVarDecl()) return false; @@ -12627,7 +12627,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { // Variables that have initialization with side-effects are required. if (VD->getInit() && VD->getInit()->HasSideEffects(*this) && // We can get a value-dependent initializer during error recovery. - (VD->getInit()->isValueDependent() || !VD->evaluateValue())) + (VD->getInit()->isValueDependent() || !VD->evaluateValue(ASTMutator))) return true; // Likewise, variables with tuple-like bindings are required if their diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 86913763ef9ff5..f49ef66cb43aea 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2546,13 +2546,14 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const { return Init.dyn_cast<EvaluatedStmt *>(); } -APValue *VarDecl::evaluateValue() const { +APValue *VarDecl::evaluateValue(EvalASTMutator *ASTMutator) const { SmallVector<PartialDiagnosticAt, 8> Notes; - return evaluateValueImpl(Notes, hasConstantInitialization()); + return evaluateValueImpl(Notes, hasConstantInitialization(), ASTMutator); } APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes, - bool IsConstantInitialization) const { + bool IsConstantInitialization, + EvalASTMutator *ASTMutator) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); const auto *Init = getInit(); @@ -2572,8 +2573,8 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes, Eval->IsEvaluating = true; ASTContext &Ctx = getASTContext(); - bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes, - IsConstantInitialization); + bool Result = Init->EvaluateAsInitializer( + Eval->Evaluated, Ctx, this, Notes, IsConstantInitialization, ASTMutator); // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't // a constant initializer if we produced notes. In that case, we can't keep @@ -2636,7 +2637,8 @@ bool VarDecl::hasConstantInitialization() const { } bool VarDecl::checkForConstantInitialization( - SmallVectorImpl<PartialDiagnosticAt> &Notes) const { + SmallVectorImpl<PartialDiagnosticAt> &Notes, + EvalASTMutator *ASTMutator) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); // If we ask for the value before we know whether we have a constant // initializer, we can compute the wrong value (for example, due to @@ -2651,7 +2653,7 @@ bool VarDecl::checkForConstantInitialization( // Evaluate the initializer to check whether it's a constant expression. Eval->HasConstantInitialization = - evaluateValueImpl(Notes, true) && Notes.empty(); + evaluateValueImpl(Notes, true, ASTMutator) && Notes.empty(); // If evaluation as a constant initializer failed, allow re-evaluation as a // non-constant initializer if we later find we want the value. diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d664c503655ba6..b9a075154b6d6f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1004,6 +1004,11 @@ namespace { EM_IgnoreSideEffects, } EvalMode; + /// Implementation of an interface for AST mutation. + /// Basically something backed by Sema, + /// or nullptr if Sema is not available. + EvalASTMutator *ASTMutator; + /// Are we checking whether the expression is a potential constant /// expression? bool checkingPotentialConstantExpression() const override { @@ -1017,7 +1022,8 @@ namespace { return CheckingForUndefinedBehavior; } - EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) + EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode, + EvalASTMutator *ASTMutator = nullptr) : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), StepsLeft(C.getLangOpts().ConstexprStepLimit), @@ -1027,13 +1033,15 @@ namespace { /*CallExpr=*/nullptr, CallRef()), EvaluatingDecl((const ValueDecl *)nullptr), EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), - HasFoldFailureDiagnostic(false), EvalMode(Mode) {} + HasFoldFailureDiagnostic(false), EvalMode(Mode), + ASTMutator(ASTMutator) {} ~EvalInfo() { discardCleanups(); } ASTContext &getASTContext() const override { return Ctx; } + EvalASTMutator *getASTMutator() const { return ASTMutator; } void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value, EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) { @@ -8328,6 +8336,12 @@ class ExprEvaluatorBase const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); + if (!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, @@ -16669,7 +16683,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, const VarDecl *VD, SmallVectorImpl<PartialDiagnosticAt> &Notes, - bool IsConstantInitialization) const { + bool IsConstantInitialization, + EvalASTMutator *ASTMutator) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); @@ -16687,7 +16702,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, (IsConstantInitialization && (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) ? EvalInfo::EM_ConstantExpression - : EvalInfo::EM_ConstantFold); + : EvalInfo::EM_ConstantFold, + ASTMutator); Info.setEvaluatingDecl(VD, Value); Info.InConstantContext = IsConstantInitialization; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 2b51765e80864a..328a8f7912a8ea 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -208,6 +208,8 @@ class SemaPPCallbacks : public PPCallbacks { } // end namespace sema } // end namespace clang +SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {} + const unsigned Sema::MaxAlignmentExponent; const uint64_t Sema::MaximumAlignment; @@ -221,7 +223,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)), @@ -2798,3 +2800,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); +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f8e5f3c6d309d6..ea6ca66f8c69e9 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14441,7 +14441,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { } } else { // Evaluate the initializer to see if it's a constant initializer. - HasConstInit = var->checkForConstantInitialization(Notes); + HasConstInit = + var->checkForConstantInitialization(Notes, getASTMutator()); } if (HasConstInit) { @@ -14549,7 +14550,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { // If this variable must be emitted, add it as an initializer for the current // module. - if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) + if (Context.DeclMustBeEmitted(var, getASTMutator()) && !ModuleScopes.empty()) Context.addModuleInitializer(ModuleScopes.back().Module, var); // Build the bindings if this is a structured binding declaration. diff --git a/clang/test/SemaCXX/constexpr-function-instantiation.cpp b/clang/test/SemaCXX/constexpr-function-instantiation.cpp new file mode 100644 index 00000000000000..43c2ced137474d --- /dev/null +++ b/clang/test/SemaCXX/constexpr-function-instantiation.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s + +namespace GH73232 { +namespace ex1 { +template <typename T> +constexpr void g(T); + +constexpr int f() { + g(0); + return 0; +} + +template <typename T> +constexpr void g(T) {} + +constexpr auto z = f(); +} // 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 \ No newline at end of file >From 57de67a735c40557ba203ad1c3e46c6791dff706 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Wed, 6 Nov 2024 18:00:39 +0300 Subject: [PATCH 2/8] Add missing newline --- clang/test/SemaCXX/constexpr-function-instantiation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/SemaCXX/constexpr-function-instantiation.cpp b/clang/test/SemaCXX/constexpr-function-instantiation.cpp index 43c2ced137474d..6c7e43f85cf6f9 100644 --- a/clang/test/SemaCXX/constexpr-function-instantiation.cpp +++ b/clang/test/SemaCXX/constexpr-function-instantiation.cpp @@ -28,4 +28,4 @@ template <typename T> constexpr void fromType() { } // #1 template <> struct QMetaTypeId<int> {}; // #20428 } // namespace ex2 -} // namespace GH73232 \ No newline at end of file +} // namespace GH73232 >From 548298c8e2d1030b5d4edb8b548609b55aeba7f0 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Wed, 6 Nov 2024 18:14:45 +0300 Subject: [PATCH 3/8] Add missing semicolon --- clang/include/clang/Sema/Sema.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1c0c6949573335..61114b7107c650 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -357,7 +357,8 @@ struct SkipBodyInfo { /// to modify AST, e.g. to instantiate templates. struct SemaASTMutator : EvalASTMutator { Sema &SemaRef; - SemaASTMutator(Sema &SemaRef) void InstantiateFunctionDefinition( + SemaASTMutator(Sema &SemaRef); + void InstantiateFunctionDefinition( SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive, bool DefinitionRequired, bool AtEndOfTU) override; }; >From 33092200cb15b3e095ee82d9001dd0e50eda668f Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Thu, 7 Nov 2024 19:35:41 +0300 Subject: [PATCH 4/8] Put ASTMutator inside ASTContext --- clang/include/clang/AST/ASTContext.h | 21 ++++++++++++++++++- clang/include/clang/AST/Decl.h | 20 +++--------------- clang/include/clang/AST/Expr.h | 3 +-- clang/lib/AST/ASTContext.cpp | 4 ++-- clang/lib/AST/Decl.cpp | 14 ++++++------- clang/lib/AST/ExprConstant.cpp | 7 +++---- clang/lib/Sema/Sema.cpp | 6 ++++++ clang/lib/Sema/SemaDecl.cpp | 4 ++-- .../constexpr-function-instantiation.cpp | 20 +++++++++++------- 9 files changed, 56 insertions(+), 43 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index d143591de9f2cd..487b1647012ca8 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -98,6 +98,7 @@ class ParentMapContext; struct ParsedTargetAttr; class Preprocessor; class ProfileList; +class Sema; class StoredDeclsMap; class TargetAttr; class TargetInfo; @@ -182,6 +183,18 @@ struct TypeInfoChars { } }; +// Interface that allows constant evaluator to mutate AST. +// When constant evaluation is triggered by Sema, it can supply a proper +// implementation of this interface. +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> { @@ -671,7 +684,11 @@ class ASTContext : public RefCountedBase<ASTContext> { /// Keeps track of the deallocated DeclListNodes for future reuse. DeclListNode *ListNodeFreeList = nullptr; + EvalASTMutator *ASTMutator = nullptr; + public: + EvalASTMutator *getASTMutator() const { return ASTMutator; } + IdentifierTable &Idents; SelectorTable &Selectors; Builtin::Context &BuiltinInfo; @@ -3258,7 +3275,7 @@ class ASTContext : public RefCountedBase<ASTContext> { /// /// \returns true if the function/var must be CodeGen'ed/deserialized even if /// it is not used. - bool DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator = nullptr); + bool DeclMustBeEmitted(const Decl *D); /// Visits all versions of a multiversioned function with the passed /// predicate. @@ -3509,6 +3526,8 @@ OPT_LIST(V) void ReleaseDeclContextMaps(); + friend void injectASTMutatorIntoASTContext(Sema &, ASTContext &); + public: enum PragmaSectionFlag : unsigned { PSF_None = 0, diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 89a2833c82194d..83a4a2f4863fab 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -78,18 +78,6 @@ class UnresolvedSetImpl; class VarTemplateDecl; enum class ImplicitParamKind; -/// Interface that allows constant evaluator to mutate AST. -/// When constant evaluation is triggered by Sema, it can supply a proper -/// implementation of this interface. -struct EvalASTMutator { - virtual ~EvalASTMutator() = default; - - virtual void - InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, - FunctionDecl *Function, bool Recursive, - bool DefinitionRequired, bool AtEndOfTU) = 0; -}; - /// The top declaration context. class TranslationUnitDecl : public Decl, public DeclContext, @@ -1367,12 +1355,11 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// Attempt to evaluate the value of the initializer attached to this /// declaration, and produce notes explaining why it cannot be evaluated. /// Returns a pointer to the value if evaluation succeeded, 0 otherwise. - APValue *evaluateValue(EvalASTMutator *ASTMutator = nullptr) const; + APValue *evaluateValue() const; private: APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes, - bool IsConstantInitialization, - EvalASTMutator *ASTMutator = nullptr) const; + bool IsConstantInitialization) const; public: /// Return the already-evaluated value of this variable's @@ -1405,8 +1392,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// constant initializer. Should only be called once, after completing the /// definition of the variable. bool - checkForConstantInitialization(SmallVectorImpl<PartialDiagnosticAt> &Notes, - EvalASTMutator *ASTMutator = nullptr) const; + checkForConstantInitialization(SmallVectorImpl<PartialDiagnosticAt> &Notes) const; void setInitStyle(InitializationStyle Style) { VarDeclBits.InitStyle = Style; diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index ccf7fd226b5a8f..466c65a9685ad3 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -734,8 +734,7 @@ class Expr : public ValueStmt { bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx, const VarDecl *VD, SmallVectorImpl<PartialDiagnosticAt> &Notes, - bool IsConstantInitializer, - EvalASTMutator *ASTMutator = nullptr) const; + bool IsConstantInitializer) const; /// EvaluateWithSubstitution - Evaluate an expression as if from the context /// of a call to the given function with the given arguments, inside an diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 0aaf23af28d3d6..69892bda42b256 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12522,7 +12522,7 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) const { basicGVALinkageForVariable(*this, VD))); } -bool ASTContext::DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator) { +bool ASTContext::DeclMustBeEmitted(const Decl *D) { if (const auto *VD = dyn_cast<VarDecl>(D)) { if (!VD->isFileVarDecl()) return false; @@ -12627,7 +12627,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator) { // Variables that have initialization with side-effects are required. if (VD->getInit() && VD->getInit()->HasSideEffects(*this) && // We can get a value-dependent initializer during error recovery. - (VD->getInit()->isValueDependent() || !VD->evaluateValue(ASTMutator))) + (VD->getInit()->isValueDependent() || !VD->evaluateValue())) return true; // Likewise, variables with tuple-like bindings are required if their diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index f49ef66cb43aea..d264441fe74e7e 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2546,14 +2546,13 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const { return Init.dyn_cast<EvaluatedStmt *>(); } -APValue *VarDecl::evaluateValue(EvalASTMutator *ASTMutator) const { +APValue *VarDecl::evaluateValue() const { SmallVector<PartialDiagnosticAt, 8> Notes; - return evaluateValueImpl(Notes, hasConstantInitialization(), ASTMutator); + return evaluateValueImpl(Notes, hasConstantInitialization()); } APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes, - bool IsConstantInitialization, - EvalASTMutator *ASTMutator) const { + bool IsConstantInitialization) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); const auto *Init = getInit(); @@ -2574,7 +2573,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes, ASTContext &Ctx = getASTContext(); bool Result = Init->EvaluateAsInitializer( - Eval->Evaluated, Ctx, this, Notes, IsConstantInitialization, ASTMutator); + Eval->Evaluated, Ctx, this, Notes, IsConstantInitialization); // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't // a constant initializer if we produced notes. In that case, we can't keep @@ -2637,8 +2636,7 @@ bool VarDecl::hasConstantInitialization() const { } bool VarDecl::checkForConstantInitialization( - SmallVectorImpl<PartialDiagnosticAt> &Notes, - EvalASTMutator *ASTMutator) const { + SmallVectorImpl<PartialDiagnosticAt> &Notes) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); // If we ask for the value before we know whether we have a constant // initializer, we can compute the wrong value (for example, due to @@ -2653,7 +2651,7 @@ bool VarDecl::checkForConstantInitialization( // Evaluate the initializer to check whether it's a constant expression. Eval->HasConstantInitialization = - evaluateValueImpl(Notes, true, ASTMutator) && Notes.empty(); + evaluateValueImpl(Notes, true) && Notes.empty(); // If evaluation as a constant initializer failed, allow re-evaluation as a // non-constant initializer if we later find we want the value. diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index b9a075154b6d6f..c167e6fa8e92a4 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8336,7 +8336,7 @@ class ExprEvaluatorBase const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); - if (!Definition && FD->getTemplateInstantiationPattern()) { + 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); @@ -16683,8 +16683,7 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, const VarDecl *VD, SmallVectorImpl<PartialDiagnosticAt> &Notes, - bool IsConstantInitialization, - EvalASTMutator *ASTMutator) const { + bool IsConstantInitialization) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); @@ -16703,7 +16702,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) ? EvalInfo::EM_ConstantExpression : EvalInfo::EM_ConstantFold, - ASTMutator); + Ctx.getASTMutator()); Info.setEvaluatingDecl(VD, Value); Info.InConstantContext = IsConstantInitialization; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 328a8f7912a8ea..ab58a54541c628 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -208,6 +208,10 @@ class SemaPPCallbacks : public PPCallbacks { } // end namespace sema } // end namespace clang +void clang::injectASTMutatorIntoASTContext(Sema &S, ASTContext &Context) { + Context.ASTMutator = S.getASTMutator(); +} + SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {} const unsigned Sema::MaxAlignmentExponent; @@ -300,6 +304,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, SemaPPCallbackHandler->set(*this); CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod()); + + injectASTMutatorIntoASTContext(*this, Context); } // Anchor Sema's type info to this TU. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index ea6ca66f8c69e9..3d95fd7013fbdd 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14442,7 +14442,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { } else { // Evaluate the initializer to see if it's a constant initializer. HasConstInit = - var->checkForConstantInitialization(Notes, getASTMutator()); + var->checkForConstantInitialization(Notes); } if (HasConstInit) { @@ -14550,7 +14550,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { // If this variable must be emitted, add it as an initializer for the current // module. - if (Context.DeclMustBeEmitted(var, getASTMutator()) && !ModuleScopes.empty()) + if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) Context.addModuleInitializer(ModuleScopes.back().Module, var); // Build the bindings if this is a structured binding declaration. diff --git a/clang/test/SemaCXX/constexpr-function-instantiation.cpp b/clang/test/SemaCXX/constexpr-function-instantiation.cpp index 6c7e43f85cf6f9..aec14e938be9f5 100644 --- a/clang/test/SemaCXX/constexpr-function-instantiation.cpp +++ b/clang/test/SemaCXX/constexpr-function-instantiation.cpp @@ -1,21 +1,27 @@ -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s -// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s -// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s +// 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); +constexpr void g(T); // #ex1-g-decl constexpr int f() { - g(0); - return 0; + g(0); // #ex1-g-call + return 0; } template <typename T> constexpr void g(T) {} -constexpr auto z = f(); +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 { >From 54da0e85a0095e6b1c40a6bb51446c4feb20e67b Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Thu, 7 Nov 2024 20:08:04 +0300 Subject: [PATCH 5/8] Don't store `ASTMutator` inside `EvalInfo` --- clang/include/clang/AST/ASTContext.h | 11 +++++++++-- clang/include/clang/AST/Decl.h | 4 ++-- clang/lib/AST/Decl.cpp | 4 ++-- clang/lib/AST/ExprConstant.cpp | 19 ++++++------------- clang/lib/Sema/Sema.cpp | 3 +++ clang/lib/Sema/SemaDecl.cpp | 3 +-- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 487b1647012ca8..b5895627213d10 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -184,8 +184,7 @@ struct TypeInfoChars { }; // Interface that allows constant evaluator to mutate AST. -// When constant evaluation is triggered by Sema, it can supply a proper -// implementation of this interface. +// Sema is the only entity that can implement this. struct EvalASTMutator { virtual ~EvalASTMutator() = default; @@ -684,9 +683,14 @@ 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; @@ -3526,6 +3530,9 @@ 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(Sema &, ASTContext &); public: diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 83a4a2f4863fab..7ff35d73df5997 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1391,8 +1391,8 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// Evaluate the initializer of this variable to determine whether it's a /// constant initializer. Should only be called once, after completing the /// definition of the variable. - bool - checkForConstantInitialization(SmallVectorImpl<PartialDiagnosticAt> &Notes) const; + bool checkForConstantInitialization( + SmallVectorImpl<PartialDiagnosticAt> &Notes) const; void setInitStyle(InitializationStyle Style) { VarDeclBits.InitStyle = Style; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index d264441fe74e7e..86913763ef9ff5 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2572,8 +2572,8 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes, Eval->IsEvaluating = true; ASTContext &Ctx = getASTContext(); - bool Result = Init->EvaluateAsInitializer( - Eval->Evaluated, Ctx, this, Notes, IsConstantInitialization); + bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes, + IsConstantInitialization); // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't // a constant initializer if we produced notes. In that case, we can't keep diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c167e6fa8e92a4..701dca6bf373ee 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1004,11 +1004,6 @@ namespace { EM_IgnoreSideEffects, } EvalMode; - /// Implementation of an interface for AST mutation. - /// Basically something backed by Sema, - /// or nullptr if Sema is not available. - EvalASTMutator *ASTMutator; - /// Are we checking whether the expression is a potential constant /// expression? bool checkingPotentialConstantExpression() const override { @@ -1022,8 +1017,7 @@ namespace { return CheckingForUndefinedBehavior; } - EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode, - EvalASTMutator *ASTMutator = nullptr) + EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), StepsLeft(C.getLangOpts().ConstexprStepLimit), @@ -1033,15 +1027,14 @@ namespace { /*CallExpr=*/nullptr, CallRef()), EvaluatingDecl((const ValueDecl *)nullptr), EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), - HasFoldFailureDiagnostic(false), EvalMode(Mode), - ASTMutator(ASTMutator) {} + HasFoldFailureDiagnostic(false), EvalMode(Mode) {} ~EvalInfo() { discardCleanups(); } ASTContext &getASTContext() const override { return Ctx; } - EvalASTMutator *getASTMutator() const { return ASTMutator; } + EvalASTMutator *getASTMutator() const { return Ctx.getASTMutator(); } void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value, EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) { @@ -8336,7 +8329,8 @@ class ExprEvaluatorBase const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); - if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getASTMutator() && !Definition && FD->getTemplateInstantiationPattern()) { + 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); @@ -16701,8 +16695,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, (IsConstantInitialization && (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) ? EvalInfo::EM_ConstantExpression - : EvalInfo::EM_ConstantFold, - Ctx.getASTMutator()); + : EvalInfo::EM_ConstantFold); Info.setEvaluatingDecl(VD, Value); Info.InConstantContext = IsConstantInitialization; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index ab58a54541c628..41eff868a3fafb 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -305,6 +305,9 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod()); + /// Initialize ASTMutator within ASTContext. + /// This is very intentionally not a part of public interface + /// of ASTContext. injectASTMutatorIntoASTContext(*this, Context); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 3d95fd7013fbdd..f8e5f3c6d309d6 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14441,8 +14441,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { } } else { // Evaluate the initializer to see if it's a constant initializer. - HasConstInit = - var->checkForConstantInitialization(Notes); + HasConstInit = var->checkForConstantInitialization(Notes); } if (HasConstInit) { >From 4d91ede45c51acc0dee61695ef19e1b8f0fcfb72 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Fri, 8 Nov 2024 18:20:22 +0300 Subject: [PATCH 6/8] Remove `Sema` from `ASTContext` --- clang/include/clang/AST/ASTContext.h | 3 +-- clang/lib/Sema/Sema.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index b5895627213d10..d2d4e13ee9b7e3 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -98,7 +98,6 @@ class ParentMapContext; struct ParsedTargetAttr; class Preprocessor; class ProfileList; -class Sema; class StoredDeclsMap; class TargetAttr; class TargetInfo; @@ -3533,7 +3532,7 @@ OPT_LIST(V) /// 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(Sema &, ASTContext &); + friend void injectASTMutatorIntoASTContext(ASTContext &, EvalASTMutator *); public: enum PragmaSectionFlag : unsigned { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 41eff868a3fafb..9469be0ba1e8cc 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -208,8 +208,9 @@ class SemaPPCallbacks : public PPCallbacks { } // end namespace sema } // end namespace clang -void clang::injectASTMutatorIntoASTContext(Sema &S, ASTContext &Context) { - Context.ASTMutator = S.getASTMutator(); +void clang::injectASTMutatorIntoASTContext(ASTContext &Context, + EvalASTMutator *ASTMutator) { + Context.ASTMutator = ASTMutator; } SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {} @@ -308,7 +309,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, /// Initialize ASTMutator within ASTContext. /// This is very intentionally not a part of public interface /// of ASTContext. - injectASTMutatorIntoASTContext(*this, Context); + injectASTMutatorIntoASTContext(Context, getASTMutator()); } // Anchor Sema's type info to this TU. >From 98945b41b480b2f51cf95b2705e9750be997d5bc Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Wed, 13 Nov 2024 18:46:58 +0300 Subject: [PATCH 7/8] Rename ASTMutator to SemaProxy --- clang/include/clang/AST/ASTContext.h | 18 +++++++++--------- clang/include/clang/Sema/Sema.h | 16 ++++++++-------- clang/lib/AST/ExprConstant.cpp | 6 +++--- clang/lib/Sema/Sema.cpp | 12 ++++++------ 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index d2d4e13ee9b7e3..944140542b11b4 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -182,10 +182,10 @@ struct TypeInfoChars { } }; -// Interface that allows constant evaluator to mutate AST. -// Sema is the only entity that can implement this. -struct EvalASTMutator { - virtual ~EvalASTMutator() = default; +// Interface that allows constant evaluator to call Sema +// and mutate the AST. +struct SemaProxy { + virtual ~SemaProxy() = default; virtual void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, @@ -684,13 +684,13 @@ class ASTContext : public RefCountedBase<ASTContext> { /// Implementation of the interface that Sema provides during its /// construction. - EvalASTMutator *ASTMutator = nullptr; + SemaProxy *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; } + SemaProxy *getSemaProxy() const { return ASTMutator; } IdentifierTable &Idents; SelectorTable &Selectors; @@ -3530,9 +3530,9 @@ 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 *); + /// that needs friendship to initialize SemaProxy without this capability + /// being exposed in the public interface of ASTContext. + friend void injectSemaProxyIntoASTContext(ASTContext &, SemaProxy *); public: enum PragmaSectionFlag : unsigned { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 61114b7107c650..7fe05a8991acfa 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -353,11 +353,11 @@ struct SkipBodyInfo { NamedDecl *New = nullptr; }; -/// Implementation of EvalASTMutator interface that enables constant evaluator -/// to modify AST, e.g. to instantiate templates. -struct SemaASTMutator : EvalASTMutator { +/// Implementation of SemaProxy interface that enables constant evaluator +/// to call Sema and modify AST, e.g. to instantiate templates. +struct SemaProxyImpl : SemaProxy { Sema &SemaRef; - SemaASTMutator(Sema &SemaRef); + SemaProxyImpl(Sema &SemaRef); void InstantiateFunctionDefinition( SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive, bool DefinitionRequired, bool AtEndOfTU) override; @@ -1053,8 +1053,8 @@ 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; } + /// Get a Sema implementation of SemaProxy interface. + SemaProxyImpl *getSemaProxy() { return &SemaProxy; } SemaAMDGPU &AMDGPU() { assert(AMDGPUPtr); @@ -1213,9 +1213,9 @@ class Sema final : public SemaBase { mutable IdentifierInfo *Ident_super; - /// EvalASTMutator implementation that can be passed to constant evaluator + /// SemaProxy implementation that can be passed to constant evaluator /// to enable it to do AST mutations, e.g. template instantiation. - SemaASTMutator ASTMutator; + SemaProxyImpl SemaProxy; std::unique_ptr<SemaAMDGPU> AMDGPUPtr; std::unique_ptr<SemaARM> ARMPtr; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 701dca6bf373ee..c118fba87e615f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1034,7 +1034,7 @@ namespace { } ASTContext &getASTContext() const override { return Ctx; } - EvalASTMutator *getASTMutator() const { return Ctx.getASTMutator(); } + SemaProxy *getSemaProxy() const { return Ctx.getSemaProxy(); } void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value, EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) { @@ -8329,9 +8329,9 @@ class ExprEvaluatorBase const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); - if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getASTMutator() && + if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getSemaProxy() && !Definition && FD->getTemplateInstantiationPattern()) { - Info.getASTMutator()->InstantiateFunctionDefinition( + Info.getSemaProxy()->InstantiateFunctionDefinition( E->getExprLoc(), const_cast<FunctionDecl *>(FD), /*Recursive=*/true, /*DefinitionRequired=*/true, /*AtEndOfTU=*/false); Body = FD->getBody(Definition); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 9469be0ba1e8cc..d5f65470a02486 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -208,12 +208,12 @@ class SemaPPCallbacks : public PPCallbacks { } // end namespace sema } // end namespace clang -void clang::injectASTMutatorIntoASTContext(ASTContext &Context, - EvalASTMutator *ASTMutator) { +void clang::injectSemaProxyIntoASTContext(ASTContext &Context, + SemaProxy *ASTMutator) { Context.ASTMutator = ASTMutator; } -SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {} +SemaProxyImpl::SemaProxyImpl(Sema &SemaRef) : SemaRef(SemaRef) {} const unsigned Sema::MaxAlignmentExponent; const uint64_t Sema::MaximumAlignment; @@ -228,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), - ASTMutator(*this), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)), + SemaProxy(*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)), @@ -309,7 +309,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, /// Initialize ASTMutator within ASTContext. /// This is very intentionally not a part of public interface /// of ASTContext. - injectASTMutatorIntoASTContext(Context, getASTMutator()); + injectSemaProxyIntoASTContext(Context, getSemaProxy()); } // Anchor Sema's type info to this TU. @@ -2811,7 +2811,7 @@ Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) { return CreateAnnotationAttr(AL, Str, Args); } -void SemaASTMutator::InstantiateFunctionDefinition( +void SemaProxyImpl::InstantiateFunctionDefinition( SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive, bool DefinitionRequired, bool AtEndOfTU) { SemaRef.InstantiateFunctionDefinition( >From b4e605ac1afe57c39d9b2b145873fbe306fd0ddb Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com> Date: Fri, 15 Nov 2024 17:58:02 +0300 Subject: [PATCH 8/8] Address feedback --- clang/include/clang/AST/ASTContext.h | 27 ++++++++++++++----- .../include/clang/Basic/DiagnosticASTKinds.td | 5 ++++ clang/include/clang/Basic/DiagnosticGroups.td | 1 + clang/include/clang/Sema/Sema.h | 14 +++------- clang/lib/AST/ASTContext.cpp | 18 ++++++++++--- clang/lib/AST/ExprConstant.cpp | 10 +++---- clang/lib/Sema/Sema.cpp | 17 ++++++------ 7 files changed, 57 insertions(+), 35 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 944140542b11b4..d05d55206c55ee 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -182,15 +182,14 @@ struct TypeInfoChars { } }; -// Interface that allows constant evaluator to call Sema -// and mutate the AST. +/// Interface that allows constant evaluator to call Sema +/// and mutate the AST. struct SemaProxy { virtual ~SemaProxy() = default; virtual void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, - FunctionDecl *Function, bool Recursive, - bool DefinitionRequired, bool AtEndOfTU) = 0; + FunctionDecl *Function) = 0; }; /// Holds long-lived AST nodes (such as types and decls) that can be @@ -684,13 +683,16 @@ class ASTContext : public RefCountedBase<ASTContext> { /// Implementation of the interface that Sema provides during its /// construction. - SemaProxy *ASTMutator = nullptr; + std::unique_ptr<SemaProxy> SemaProxyPtr; 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. - SemaProxy *getSemaProxy() const { return ASTMutator; } + SemaProxy &getSemaProxy() const { + assert(SemaProxyPtr); + return *SemaProxyPtr; + } IdentifierTable &Idents; SelectorTable &Selectors; @@ -3532,7 +3534,8 @@ OPT_LIST(V) /// This is a function that is implemented in the Sema layer, /// that needs friendship to initialize SemaProxy without this capability /// being exposed in the public interface of ASTContext. - friend void injectSemaProxyIntoASTContext(ASTContext &, SemaProxy *); + friend void injectSemaProxyIntoASTContext(ASTContext &, + std::unique_ptr<SemaProxy>); public: enum PragmaSectionFlag : unsigned { @@ -3601,6 +3604,16 @@ inline Selector GetUnarySelector(StringRef name, ASTContext &Ctx) { return Ctx.Selectors.getSelector(1, &II); } +/// Placeholder implementation that issues a diagnostic on any usage. +struct UnimplementedSemaProxy : SemaProxy { + ASTContext &Ctx; + explicit UnimplementedSemaProxy(ASTContext &); + virtual ~UnimplementedSemaProxy() = default; + + void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function) override; +}; + } // namespace clang // operator new and delete aren't allowed inside namespaces. diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 6a658cf14356f5..58ded8d0b2a7fc 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -1025,4 +1025,9 @@ def warn_unpacked_field def warn_unaligned_access : Warning< "field %1 within %0 is less aligned than %2 and is usually due to %0 being " "packed, which can lead to unaligned accesses">, InGroup<UnalignedAccess>, DefaultIgnore; + +def warn_side_effects_on_ast_unavailable : Warning< + "%select{instantiation of a function template}0" + " did not happen, because internal program representation is immutable">, + InGroup<SideEffectsUnavailable>; } diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 72eada50a56cc9..4119c90352137f 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -629,6 +629,7 @@ def Packed : DiagGroup<"packed", [PackedNonPod]>; def PaddedBitField : DiagGroup<"padded-bitfield">; def Padded : DiagGroup<"padded", [PaddedBitField]>; def UnalignedAccess : DiagGroup<"unaligned-access">; +def SideEffectsUnavailable : DiagGroup<"side-effects-unavailable">; def PessimizingMove : DiagGroup<"pessimizing-move">; def ReturnStdMove : DiagGroup<"return-std-move">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7fe05a8991acfa..dfde0e69323a65 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -357,10 +357,9 @@ struct SkipBodyInfo { /// to call Sema and modify AST, e.g. to instantiate templates. struct SemaProxyImpl : SemaProxy { Sema &SemaRef; - SemaProxyImpl(Sema &SemaRef); - void InstantiateFunctionDefinition( - SourceLocation PointOfInstantiation, FunctionDecl *Function, - bool Recursive, bool DefinitionRequired, bool AtEndOfTU) override; + explicit SemaProxyImpl(Sema &SemaRef); + void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function) override; }; /// Describes the result of template argument deduction. @@ -1053,9 +1052,6 @@ class Sema final : public SemaBase { /// CurContext - This is the current declaration context of parsing. DeclContext *CurContext; - /// Get a Sema implementation of SemaProxy interface. - SemaProxyImpl *getSemaProxy() { return &SemaProxy; } - SemaAMDGPU &AMDGPU() { assert(AMDGPUPtr); return *AMDGPUPtr; @@ -1213,10 +1209,6 @@ class Sema final : public SemaBase { mutable IdentifierInfo *Ident_super; - /// SemaProxy implementation that can be passed to constant evaluator - /// to enable it to do AST mutations, e.g. template instantiation. - SemaProxyImpl SemaProxy; - std::unique_ptr<SemaAMDGPU> AMDGPUPtr; std::unique_ptr<SemaARM> ARMPtr; std::unique_ptr<SemaAVR> AVRPtr; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 69892bda42b256..b61e064bc255bd 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -51,6 +51,7 @@ #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CommentOptions.h" +#include "clang/Basic/DiagnosticAST.h" #include "clang/Basic/ExceptionSpecificationType.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" @@ -902,9 +903,11 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, LangOpts.XRayNeverInstrumentFiles, LangOpts.XRayAttrListFiles, SM)), ProfList(new ProfileList(LangOpts.ProfileListFiles, SM)), - PrintingPolicy(LOpts), Idents(idents), Selectors(sels), - BuiltinInfo(builtins), TUKind(TUKind), DeclarationNames(*this), - Comments(SM), CommentCommandTraits(BumpAlloc, LOpts.CommentOpts), + PrintingPolicy(LOpts), + SemaProxyPtr(std::make_unique<UnimplementedSemaProxy>(*this)), + Idents(idents), Selectors(sels), BuiltinInfo(builtins), TUKind(TUKind), + DeclarationNames(*this), Comments(SM), + CommentCommandTraits(BumpAlloc, LOpts.CommentOpts), CompCategories(this_()), LastSDM(nullptr, 0) { addTranslationUnitDecl(); } @@ -14492,3 +14495,12 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, ThunksToBeAbbreviated[VirtualMethodDecl] = std::move(SimplifiedThunkNames); return Result; } + +UnimplementedSemaProxy::UnimplementedSemaProxy(ASTContext &Ctx) : Ctx(Ctx) {} + +void UnimplementedSemaProxy::InstantiateFunctionDefinition( + SourceLocation PointOfInstantiation, FunctionDecl *Function) { + Ctx.getDiagnostics().Report(PointOfInstantiation, + diag::warn_side_effects_on_ast_unavailable) + << 0; +} diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c118fba87e615f..4d523d9f15e9c4 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1034,7 +1034,7 @@ namespace { } ASTContext &getASTContext() const override { return Ctx; } - SemaProxy *getSemaProxy() const { return Ctx.getSemaProxy(); } + SemaProxy &getSemaProxy() const { return Ctx.getSemaProxy(); } void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value, EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) { @@ -8329,11 +8329,9 @@ class ExprEvaluatorBase const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); - if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getSemaProxy() && - !Definition && FD->getTemplateInstantiationPattern()) { - Info.getSemaProxy()->InstantiateFunctionDefinition( - E->getExprLoc(), const_cast<FunctionDecl *>(FD), - /*Recursive=*/true, /*DefinitionRequired=*/true, /*AtEndOfTU=*/false); + if (!Definition && FD->getTemplateInstantiationPattern()) { + Info.getSemaProxy().InstantiateFunctionDefinition( + E->getExprLoc(), const_cast<FunctionDecl *>(FD)); Body = FD->getBody(Definition); } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index d5f65470a02486..793e6ed3d059bd 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -209,8 +209,8 @@ class SemaPPCallbacks : public PPCallbacks { } // end namespace clang void clang::injectSemaProxyIntoASTContext(ASTContext &Context, - SemaProxy *ASTMutator) { - Context.ASTMutator = ASTMutator; + std::unique_ptr<SemaProxy> ProxyPtr) { + Context.SemaProxyPtr = std::move(ProxyPtr); } SemaProxyImpl::SemaProxyImpl(Sema &SemaRef) : SemaRef(SemaRef) {} @@ -228,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), - SemaProxy(*this), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*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)), @@ -306,10 +306,11 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod()); - /// Initialize ASTMutator within ASTContext. + /// Initialize SemaProxyPtr within ASTContext. /// This is very intentionally not a part of public interface /// of ASTContext. - injectSemaProxyIntoASTContext(Context, getSemaProxy()); + injectSemaProxyIntoASTContext(Context, + std::make_unique<SemaProxyImpl>(*this)); } // Anchor Sema's type info to this TU. @@ -2812,8 +2813,8 @@ Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) { } void SemaProxyImpl::InstantiateFunctionDefinition( - SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive, - bool DefinitionRequired, bool AtEndOfTU) { + SourceLocation PointOfInstantiation, FunctionDecl *Function) { SemaRef.InstantiateFunctionDefinition( - PointOfInstantiation, Function, Recursive, DefinitionRequired, AtEndOfTU); + PointOfInstantiation, Function, /*Recursive=*/true, + /*DefinitionRequired=*/true, /*AtEndOfTU=*/false); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits