diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 668097236fe97..f543530e90058 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -270,13 +270,13 @@ Improvements to Clang's diagnostics when accessing a member function on a past-the-end array element. (#GH179128) -- Added a missing space to the FixIt for the ``implicit-int`` group of diagnostics and +- Added a missing space to the FixIt for the ``implicit-int`` group of diagnostics and made sure that only one such diagnostic and FixIt is emitted per declaration group. (#GH179354) - The ``-Wloop-analysis`` warning has been extended to catch more cases of variable modification inside lambda expressions (#GH132038). -- Clang now emits ``-Wsizeof-pointer-memaccess`` when snprintf/vsnprintf use the sizeof +- Clang now emits ``-Wsizeof-pointer-memaccess`` when snprintf/vsnprintf use the sizeof the destination buffer(dynamically allocated) in the len parameter(#GH162366) Improvements to Clang's time-trace @@ -322,8 +322,9 @@ Bug Fixes to C++ Support template parameters when one of its parameters is also a pack. (#GH181166) - Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639) - Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741) +- Instantiate constexpr functions as needed before they are evaluated. (#GH73232) (#GH35052) (#GH100897) - Fixed a crash when pack expansions are used as arguments for non-pack parameters of built-in templates. (#GH180307) -- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable +- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable when used inside decltype in the return type. (#GH180460) - Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in ``constexpr``. (#GH180044) - Fixed a crash on ``typeid`` of incomplete local types during template instantiation. (#GH63242), (#GH176397) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 05302c30d18d1..6a55a7862bb4d 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -45,6 +45,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/TypeSize.h" +#include #include namespace llvm { @@ -216,6 +217,15 @@ struct TypeInfoChars { } }; +/// Interface that allows constant evaluator to call Sema +/// and mutate the AST. +struct SemaProxy { + virtual ~SemaProxy() = default; + + virtual void + instantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function) = 0; +}; struct PFPField { CharUnits Offset; FieldDecl *Field; @@ -793,6 +803,8 @@ class ASTContext : public RefCountedBase { /// Keeps track of the deallocated DeclListNodes for future reuse. DeclListNode *ListNodeFreeList = nullptr; + std::unique_ptr SemaProxyPtr; + public: IdentifierTable &Idents; SelectorTable &Selectors; @@ -1427,6 +1439,16 @@ class ASTContext : public RefCountedBase { /// with this AST context, if any. ASTMutationListener *getASTMutationListener() const { return Listener; } + /// Returns a SemaProxy*, i.e an object through which interact with Sema. + /// This will return null, unless setSemaProxy has been called. + /// As Sema is only available during parsing, getSemaProxy will return null + /// during CodeGen and when using the AST from tooling. + SemaProxy *getSemaProxy() { return SemaProxyPtr.get(); } + + /// Set the SemaProxy instance + /// This function is called from Sema during the initial parsing of a TU. + void setSemaProxy(std::unique_ptr Proxy); + void PrintStats() const; const SmallVectorImpl& getTypes() const { return Types; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 13a412914f5ce..370ab39e316df 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -922,6 +922,9 @@ class Sema final : public SemaBase { /// initialized but before it parses anything. void Initialize(); + void registerSemaProxy(); + void unregisterSemaProxy(); + /// This virtual key function only exists to limit the emission of debug info /// describing the Sema class. GCC and Clang only emit debug info for a class /// with a vtable when the vtable is emitted. Sema is final and not diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 5fbdff280073f..da6b38595cb9b 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -963,6 +963,11 @@ ASTContext::setExternalSource(IntrusiveRefCntPtr Source) { ExternalSource = std::move(Source); } +void ASTContext::setSemaProxy(std::unique_ptr Proxy) { + assert((!SemaProxyPtr || !Proxy) && "SemaProxy already set"); + SemaProxyPtr = std::move(Proxy); +} + void ASTContext::PrintStats() const { llvm::errs() << "\n*** AST Context Stats:\n"; llvm::errs() << " " << Types.size() << " types total.\n"; diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index ebc7220aa5671..03cd5871519a1 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1616,13 +1616,31 @@ bool CheckBitCast(InterpState &S, CodePtr OpPC, const Type *TargetType, return true; } -static void compileFunction(InterpState &S, const Function *Func) { - const FunctionDecl *Definition = Func->getDecl()->getDefinition(); - if (!Definition) +static void compileFunction(InterpState &S, const Function *Func, + CodePtr OpPC) { + + const FunctionDecl *Fn = Func->getDecl(); + + // [C++26] [temp.inst] p5 + // [...] the function template specialization is implicitly instantiated + // when the specialization is referenced in a context that requires a function + // definition to exist or if the existence of the definition affects the + // semantics of the program. + if (FunctionDefinitionCanBeLazilyInstantiated(Func->getDecl()) && + S.inConstantContext() && !S.PerformingTrialEvaluation && + !S.checkingPotentialConstantExpression()) { + SemaProxy *SP = S.getASTContext().getSemaProxy(); + if (!SP) + return; + SP->instantiateFunctionDefinition(S.Current->getLocation(OpPC), + const_cast(Fn)); + } + Fn = Fn->getDefinition(); + if (!Fn) return; Compiler(S.getContext(), S.P) - .compileFunc(Definition, const_cast(Func)); + .compileFunc(Fn, const_cast(Func)); } bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func, @@ -1648,7 +1666,7 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func, } if (!Func->isFullyCompiled()) - compileFunction(S, Func); + compileFunction(S, Func, OpPC); if (!CheckCallable(S, OpPC, Func)) return false; @@ -1725,7 +1743,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func, } if (!Func->isFullyCompiled()) - compileFunction(S, Func); + compileFunction(S, Func, OpPC); if (!CheckCallable(S, OpPC, Func)) return cleanup(); @@ -1934,7 +1952,7 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, // because the Call/CallVirt below might access the instance pointer // but the Function's information about them is wrong. if (!F->isFullyCompiled()) - compileFunction(S, F); + compileFunction(S, F, OpPC); if (!CheckCallable(S, OpPC, F)) return false; diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index fd69559af5917..a9978ea7db0fc 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -24,6 +24,7 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) { InConstantContext = Parent.InConstantContext; + PerformingTrialEvaluation = Parent.PerformingTrialEvaluation; CheckingPotentialConstantExpression = Parent.CheckingPotentialConstantExpression; CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; @@ -38,6 +39,7 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) { InConstantContext = Parent.InConstantContext; + PerformingTrialEvaluation = Parent.PerformingTrialEvaluation; CheckingPotentialConstantExpression = Parent.CheckingPotentialConstantExpression; CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h index a720033c7914b..c4a4071f6f359 100644 --- a/clang/lib/AST/ByteCode/State.h +++ b/clang/lib/AST/ByteCode/State.h @@ -187,6 +187,10 @@ class State { /// is set; this is used when evaluating ICEs in C. bool CheckingForUndefinedBehavior = false; + /// Whether we are performing trial evaluation, i.e when evaluating the + /// initializer of a constant-initialized variable. + bool PerformingTrialEvaluation = false; + EvaluationMode EvalMode; ASTContext &Ctx; Expr::EvalStatus &EvalStatus; diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h index 619c79a1408f3..45c7db846fc70 100644 --- a/clang/lib/AST/ExprConstShared.h +++ b/clang/lib/AST/ExprConstShared.h @@ -29,6 +29,7 @@ class LangOptions; class ASTContext; class CharUnits; class Expr; +class FunctionDecl; } // namespace clang using namespace clang; /// Values returned by __builtin_classify_type, chosen to match the values @@ -89,4 +90,7 @@ std::optional EvalScalarMinMaxFp(const llvm::APFloat &A, const llvm::APFloat &B, std::optional RoundingMode, bool IsMin); +/// Whether we can instantiate FD during constant evaluation +bool FunctionDefinitionCanBeLazilyInstantiated(const FunctionDecl *FD); + #endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index feea97cd67534..9d2ac4c564fff 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -43,6 +43,7 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CurrentSourceLocExprScope.h" +#include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/InferAlloc.h" #include "clang/AST/OSLog.h" @@ -53,6 +54,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APFixedPoint.h" @@ -6958,6 +6960,33 @@ static bool handleTrivialCopy(EvalInfo &Info, const ParmVarDecl *Param, CopyObjectRepresentation); } +bool FunctionDefinitionCanBeLazilyInstantiated(const FunctionDecl *FD) { + return !FD->isDefined() && FD->isImplicitlyInstantiable() && + FD->isConstexpr(); +} + +static void TryInstantiateFunctionBeforeCall(const FunctionDecl *FD, + EvalInfo &Info, + SourceLocation Loc) { + + // [C++26] [temp.inst] p5 + // [...] the function template specialization is implicitly instantiated + // when the specialization is referenced in a context that requires a function + // definition to exist or if the existence of the definition affects the + // semantics of the program. + + if (FunctionDefinitionCanBeLazilyInstantiated(FD) && Info.InConstantContext && + !Info.PerformingTrialEvaluation && + !Info.checkingPotentialConstantExpression()) { + + SemaProxy *SP = Info.getASTContext().getSemaProxy(); + // Try to instantiate the definition if Sema is available + // (i.e during the initial parse of the TU). + if (SP) + SP->instantiateFunctionDefinition(Loc, const_cast(FD)); + } +} + /// Evaluate a function call. static bool HandleFunctionCall(SourceLocation CallLoc, const FunctionDecl *Callee, @@ -7351,6 +7380,8 @@ static bool HandleDestructionImpl(EvalInfo &Info, SourceRange CallRange, if (!Info.CheckCallLimit(CallRange.getBegin())) return false; + TryInstantiateFunctionBeforeCall(DD, Info, CallRange.getBegin()); + const FunctionDecl *Definition = nullptr; const Stmt *Body = DD->getBody(Definition); @@ -8816,10 +8847,13 @@ class ExprEvaluatorBase CallScope.destroy(); } - const FunctionDecl *Definition = nullptr; - Stmt *Body = FD->getBody(Definition); SourceLocation Loc = E->getExprLoc(); + TryInstantiateFunctionBeforeCall(FD, Info, Loc); + + const FunctionDecl *Definition = nullptr; + const Stmt *Body = FD->getBody(Definition); + // Treat the object argument as `this` when evaluating defaulted // special menmber functions if (FD->hasCXXExplicitFunctionObjectParameter()) @@ -11337,8 +11371,10 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, return handleDefaultInitValue(T, Result); } + TryInstantiateFunctionBeforeCall(FD, Info, E->getBeginLoc()); + const FunctionDecl *Definition = nullptr; - auto Body = FD->getBody(Definition); + const Stmt *Body = FD->getBody(Definition); if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body)) return false; @@ -11378,8 +11414,9 @@ bool RecordExprEvaluator::VisitCXXInheritedCtorInitExpr( if (FD->isInvalidDecl() || FD->getParent()->isInvalidDecl()) return false; + TryInstantiateFunctionBeforeCall(FD, Info, E->getBeginLoc()); const FunctionDecl *Definition = nullptr; - auto Body = FD->getBody(Definition); + const Stmt *Body = FD->getBody(Definition); if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body)) return false; @@ -20888,6 +20925,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, : EvaluationMode::ConstantFold); Info.setEvaluatingDecl(VD, Value); Info.InConstantContext = IsConstantInitialization; + Info.PerformingTrialEvaluation = + !VD->isConstexpr() && !VD->hasAttr(); SourceLocation DeclLoc = VD->getLocation(); QualType DeclTy = VD->getType(); diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index 1329391fd0904..eb84fa46df0ad 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -44,9 +44,13 @@ IncrementalParser::IncrementalParser(CompilerInstance &Instance, External->StartTranslationUnit(Consumer); P->Initialize(); + S.registerSemaProxy(); } -IncrementalParser::~IncrementalParser() { P.reset(); } +IncrementalParser::~IncrementalParser() { + S.unregisterSemaProxy(); + P.reset(); +} llvm::Expected IncrementalParser::ParseOrWrapTopLevelDecl() { diff --git a/clang/lib/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp index c8ee625eb57ad..01e17a563838a 100644 --- a/clang/lib/Parse/ParseAST.cpp +++ b/clang/lib/Parse/ParseAST.cpp @@ -21,6 +21,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaConsumer.h" #include "clang/Sema/TemplateInstCallback.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/TimeProfiler.h" #include @@ -161,18 +162,27 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) { return M; }); P.Initialize(); + + S.registerSemaProxy(); + Parser::DeclGroupPtrTy ADecl; Sema::ModuleImportState ImportState; EnterExpressionEvaluationContext PotentiallyEvaluated( S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); - for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; - AtEOF = P.ParseTopLevelDecl(ADecl, ImportState)) { - // If we got a null return and something *was* parsed, ignore it. This - // is due to a top-level semicolon, an action override, or a parse error - // skipping something. - if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) - return; + { + // Remove the sema proxy after the initial parse. + llvm::scope_exit SemaProxyScope([&S]() { S.unregisterSemaProxy(); }); + + for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; + AtEOF = P.ParseTopLevelDecl(ADecl, ImportState)) { + + // If we got a null return and something *was* parsed, ignore it. This + // is due to a top-level semicolon, an action override, or a parse error + // skipping something. + if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) + return; + } } } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 3065b5e1e66d3..f27315c6d1bda 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -80,6 +80,8 @@ using namespace clang; using namespace sema; +static std::unique_ptr getSemaProxyImplementation(Sema &SemaRef); + SourceLocation Sema::getLocForEndOfToken(SourceLocation Loc, unsigned Offset) { return Lexer::getLocForEndOfToken(Loc, Offset, SourceMgr, LangOpts); } @@ -622,6 +624,15 @@ Sema::~Sema() { SemaPPCallbackHandler->reset(); } +void Sema::registerSemaProxy() { + // Let the AST context rely on Sema for + // AST mutation features that require semantic analysis + // (lazy instantiation, reflection, etc). + Context.setSemaProxy(getSemaProxyImplementation(*this)); +} + +void Sema::unregisterSemaProxy() { Context.setSemaProxy({}); } + void Sema::runWithSufficientStackSpace(SourceLocation Loc, llvm::function_ref Fn) { StackHandler.runWithSufficientStackSpace(Loc, Fn); @@ -2978,3 +2989,21 @@ Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) { return CreateAnnotationAttr(AL, Str, Args); } + +class SemaProxyImplementation final : public SemaProxy { +private: + Sema &SemaRef; + +public: + SemaProxyImplementation(Sema &SemaRef) : SemaRef(SemaRef) {} + void instantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function) override { + SemaRef.InstantiateFunctionDefinition( + PointOfInstantiation, Function, /*Recursive=*/true, + /*DefinitionRequired=*/true, /*AtEndOfTU=*/false); + } +}; + +std::unique_ptr getSemaProxyImplementation(Sema &SemaRef) { + return std::make_unique(SemaRef); +} diff --git a/clang/test/SemaCXX/constexpr-late-instantiation.cpp b/clang/test/SemaCXX/constexpr-late-instantiation.cpp index 9aec0c90e61dc..f6e13d762ae1a 100644 --- a/clang/test/SemaCXX/constexpr-late-instantiation.cpp +++ b/clang/test/SemaCXX/constexpr-late-instantiation.cpp @@ -1,16 +1,167 @@ -// RUN: %clang_cc1 %s -fsyntax-only -verify -// RUN: %clang_cc1 %s -fexperimental-new-constant-interpreter -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++14 -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify + +// RUN: %clang_cc1 %s -std=c++14 -fsyntax-only -fexperimental-new-constant-interpreter -verify +// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -fexperimental-new-constant-interpreter -verify +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fexperimental-new-constant-interpreter -verify template -constexpr T foo(T a); // expected-note {{declared here}} +constexpr T foo(T a); // expected-note {{explicit instantiation refers here}} int main() { int k = foo(5); // Ok - constexpr int j = // expected-error {{constexpr variable 'j' must be initialized by a constant expression}} - foo(5); // expected-note {{undefined function 'foo' cannot be used in a constant expression}} + constexpr int j = + foo(5); // expected-error {{explicit instantiation of undefined function template 'foo'}} \ + // expected-error {{constexpr variable 'j' must be initialized by a constant expression}} } template constexpr T foo(T a) { return a; } + + +namespace GH73232 { +namespace ex1 { +template +constexpr void g(T); + +constexpr int f() { + g(0); + return 0; +} + +template +constexpr void g(T) {} + +constexpr auto z = f(); +} + +namespace ex2 { +template constexpr static void fromType(); + +void registerConverter() { fromType(); } +template struct QMetaTypeId {}; +template constexpr void fromType() { + (void)QMetaTypeId{}; +} +template <> struct QMetaTypeId {}; +} // namespace ex2 + +namespace ex3 { + +#if __cplusplus > 202302L +struct A { + consteval A(int i) { + chk(i); + } + constexpr void chk(auto) {} +}; +A a{1}; + +#endif + +} + +} // namespace GH73232 + + +namespace GH156255 { + +class X +{ +public: + constexpr int f( int x ) const + { + return g( x ); + } + +private: + + template + constexpr T g( T x ) const + { + return x; + } +}; + +// check that g is instantiated here. +constexpr int x = X().f( 1 ); +} + +#if __cplusplus > 202002L + +namespace instantiation_context_lookup { + +static constexpr int i = 42; +static constexpr int v = 8; + + +constexpr int f(auto); + +constexpr int g(int v = 42) { + static constexpr int i = 1; + return f(1); + return 0; +} + +constexpr int f(auto) { + return i + v; +} + +static_assert(g() == 50); + +} + +namespace GH35052 { + +template +constexpr int func(F f) { + if constexpr (f(1UL)) { + return 1; + } + return 0; +} + +int test() { + auto predicate = [](auto v) constexpr -> bool { return v == 1; }; + return func(predicate); // check that "predicate" is instantiated. +} + + +} // namespace GH35052 + +namespace GH115118 { + +struct foo { + foo(const foo&) = default; + foo(auto) + requires([]() -> bool { return true; }()) + {} +}; + +struct bar { + foo x; // check that the lambda gets instantiated. +}; + +} // namespace GH115118 + + +namespace GH100897 { + +template +constexpr auto foo() noexcept { + constexpr auto extract_size = []() constexpr -> int { + return 1; + }; + + constexpr int result = extract_size.template operator()(); + return result; +} + +void test() { foo(); } // check that the lambda gets instantiated. + +} // namespace GH100897 + +#endif diff --git a/clang/test/SemaCXX/constexpr-subobj-initialization.cpp b/clang/test/SemaCXX/constexpr-subobj-initialization.cpp index f0252df1e2ce1..c2e51d3920f6a 100644 --- a/clang/test/SemaCXX/constexpr-subobj-initialization.cpp +++ b/clang/test/SemaCXX/constexpr-subobj-initialization.cpp @@ -47,12 +47,13 @@ constexpr Bar bb; // expected-error {{must be initialized by a constant expressi template struct Baz { - constexpr Baz(); // expected-note {{declared here}} + constexpr Baz(); // expected-note {{explicit instantiation refers here}} }; struct Quux : Baz, private Bar { int i; - constexpr Quux() : i(12) {} // expected-note {{undefined constructor 'Baz' cannot be used in a constant expression}} + constexpr Quux() : i(12) {} // expected-error {{explicit instantiation of undefined member function 'Baz' of class template 'Baz'}} \ + // expected-note {{subexpression not valid in a constant expression}} }; constexpr Quux qx; // expected-error {{must be initialized by a constant expression}} \ diff --git a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp index 39097d17441f7..a2c5b583904f2 100644 --- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp +++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp @@ -75,10 +75,11 @@ struct a { a aa(fdupe((f(7)))); template -constexpr int foo(T t); // expected-note {{declared here}} +constexpr int foo(T t); // expected-note {{explicit instantiation refers here}} a bb(f(foo(7))); // expected-error{{call to immediate function 'f' is not a constant expression}} \ - // expected-note{{undefined function 'foo' cannot be used in a constant expression}} + // expected-error{{explicit instantiation of undefined function template 'foo'}} \ + // expected-note{{subexpression not valid in a constant expression}} }