sepavloff created this revision. Previous behavior was to instantiate constexpr function always, even if it is used in the body of a template. For instance, in the following code:
template<typename T> constexpr T foo(T x) { return x; } template<typename T> T bar(T x) { return foo(0); } the function template specialization foo<int> was instantiated because it was used in the body of template bar<T>. It is a violation of C++ Standard requirement that an implementation shall not implicitly instantiate a function template unless such instantiation is required ([temp.inst]p8). This behavior is different from the case of non-constexpr function, which indeed are instantiated when needed. With this change constexpr functions are instantiated in the same way as non-constexpr functions - when they are actually used. To make it possible constant evaluator must be able to instantiate function definition. It is provided by ASTContext, which in turn make the instantiation with help of Sema. This change fixes PR33561. https://reviews.llvm.org/D36353 Files: include/clang/AST/ASTContext.h lib/AST/ASTContext.cpp lib/AST/ExprConstant.cpp lib/Sema/Sema.cpp lib/Sema/SemaExpr.cpp test/SemaTemplate/instantiate-constexpr-function.cpp
Index: test/SemaTemplate/instantiate-constexpr-function.cpp =================================================================== --- /dev/null +++ test/SemaTemplate/instantiate-constexpr-function.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c++14 -verify %s +// expected-no-diagnostics + +template<typename T> constexpr void func_01(T) { + func_01(0); +} + + +template<typename T> constexpr unsigned func_02a() { + return sizeof(T); +} +template<typename T> constexpr T func_02b(T x) { + return x + func_02a<int>(); +} +constexpr long val_02 = func_02b(14L); + + +template<typename T> constexpr T func_03(T) { + return T::xyz; +} +template<typename T> T func_04(T x) { + return x; +} +template<> constexpr long func_04(long x) { + return 66; +} +constexpr long var_04 = func_04(0L); +static_assert(var_04 == 66, "error"); + + +template<typename T> struct C_05 { + constexpr T func_05() { return T::xyz; } +}; +C_05<int> var_05; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -13629,17 +13629,12 @@ } } - if (!AlreadyInstantiated || Func->isConstexpr()) { + if (!AlreadyInstantiated) { if (isa<CXXRecordDecl>(Func->getDeclContext()) && cast<CXXRecordDecl>(Func->getDeclContext())->isLocalClass() && CodeSynthesisContexts.size()) PendingLocalImplicitInstantiations.push_back( std::make_pair(Func, PointOfInstantiation)); - else if (Func->isConstexpr()) - // Do not defer instantiations of constexpr functions, to avoid the - // expression evaluator needing to call back into Sema if it sees a - // call to such a function. - InstantiateFunctionDefinition(PointOfInstantiation, Func); else { Func->setInstantiationIsPending(true); PendingInstantiations.push_back(std::make_pair(Func, Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -113,6 +113,20 @@ } // end namespace sema } // end namespace clang +namespace { +class SemaTemplateInstantiator : public ASTContext::InstantiationHelper { + Sema &S; +public: + ~SemaTemplateInstantiator() {} + SemaTemplateInstantiator(Sema &S) : S(S) {} + bool instantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function) override { + S.InstantiateFunctionDefinition(PointOfInstantiation, Function); + return Function->hasBody(); + } +}; +} + Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter) : ExternalSource(nullptr), isMultiplexExternalSource(false), @@ -171,6 +185,8 @@ SemaPPCallbackHandler = Callbacks.get(); PP.addPPCallbacks(std::move(Callbacks)); SemaPPCallbackHandler->set(*this); + + Context.setInstantiator(new SemaTemplateInstantiator(*this)); } void Sema::addImplicitTypedef(StringRef Name, QualType T) { @@ -359,6 +375,9 @@ // by the preprocessor. SemaPPCallbackHandler->reset(); + ASTContext::InstantiationHelper *Inst = Context.removeInstantiator(); + delete Inst; + assert(DelayedTypos.empty() && "Uncorrected typos!"); } Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -2564,6 +2564,30 @@ return false; } +/// Returns body of the function described by the specified declaration, +/// instantiating its definition if necessary. +/// +/// \param[in] FD The function which body is retrieved. +/// \param[in] Loc Location that becomes a point of instantiation. +/// \param[out] Definition The redeclaration that provides the body. +/// +/// Behaves like FinctionDecl::getBody, but also can implicitly instantiate +/// function body. +/// +static Stmt *getFunctionBody(const FunctionDecl *FD, SourceLocation Loc, + const FunctionDecl *&Definition) { + Definition = nullptr; + Stmt *Body = FD->getBody(Definition); + // Try instantiation function body is possible. Do it only for constexpr + // declarations, others are not allowed in constant expressions. + if (!Body && FD->isImplicitlyInstantiable() && FD->isConstexpr()) { + FD->getASTContext().instantiateFunctionDefinition(Loc, + const_cast<FunctionDecl *>(FD)); + Body = FD->getBody(Definition); + } + return Body; +} + /// Kinds of access we can perform on an object, for diagnostics. enum AccessKinds { AK_Read, @@ -4715,7 +4739,7 @@ return Error(E, diag::note_constexpr_virtual_call); const FunctionDecl *Definition = nullptr; - Stmt *Body = FD->getBody(Definition); + Stmt *Body = getFunctionBody(FD, Callee->getLocStart(), Definition); if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body) || !HandleFunctionCall(E->getExprLoc(), Definition, This, Args, Body, Info, @@ -6285,7 +6309,7 @@ } const FunctionDecl *Definition = nullptr; - auto Body = FD->getBody(Definition); + Stmt *Body = getFunctionBody(FD, E->getLocStart(), Definition); if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body)) return false; @@ -6317,7 +6341,7 @@ return false; const FunctionDecl *Definition = nullptr; - auto Body = FD->getBody(Definition); + Stmt *Body = getFunctionBody(FD, E->getExprLoc(), Definition); if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body)) return false; @@ -10714,8 +10738,10 @@ HandleConstructorCall(&VIE, This, Args, CD, Info, Scratch); } else { SourceLocation Loc = FD->getLocation(); + const FunctionDecl *Definition; + Stmt *Body = getFunctionBody(FD, Loc, Definition); HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : nullptr, - Args, FD->getBody(), Info, Scratch, nullptr); + Args, Body, Info, Scratch, nullptr); } return Diags.empty(); Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -9596,6 +9596,28 @@ return (*AddrSpaceMap)[AS]; } +ASTContext::InstantiationHelper::~InstantiationHelper() { +} + +void ASTContext::setInstantiator(ASTContext::InstantiationHelper *Inst) { + Instantiator = Inst; +} + +ASTContext::InstantiationHelper *ASTContext::removeInstantiator() { + InstantiationHelper *Res = Instantiator; + Instantiator = nullptr; + return Res; +} + +bool ASTContext::instantiateFunctionDefinition( + SourceLocation PointOfInstantiation, + FunctionDecl *Function) { + if (!Instantiator) + return false; + return Instantiator->instantiateFunctionDefinition(PointOfInstantiation, + Function); +} + // Explicitly instantiate this in case a Redeclarable<T> is used from a TU that // doesn't include ASTContext.h template Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -1883,6 +1883,29 @@ CanQualType getFromTargetType(unsigned Type) const; TypeInfo getTypeInfoImpl(const Type *T) const; + + //===--------------------------------------------------------------------===// + // Template instantiation. + //===--------------------------------------------------------------------===// +public: + + /// Helper class that provides interface to template instantiation facility. + class InstantiationHelper { + public: + virtual ~InstantiationHelper(); + virtual bool instantiateFunctionDefinition( + SourceLocation PointOfInstantiation, + FunctionDecl *Function) = 0; + }; + + void setInstantiator(InstantiationHelper *Helper); + InstantiationHelper *removeInstantiator(); + bool instantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function); +private: + InstantiationHelper *Instantiator = nullptr; + + //===--------------------------------------------------------------------===// // Type Predicates. //===--------------------------------------------------------------------===//
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits