diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8d2b60dd75acfe..7d64647433d92a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -793,6 +793,11 @@ Bug Fixes to C++ Support - Fix crash when parsing nested requirement. Fixes: (`#73112 `_) +- Clang now immediately instantiates function template specializations + at the end of the definition of the corresponding function template + when the definition appears after the first point of instantiation. + (`#73232 `_) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed an import failure of recursive friend class template. diff --git a/clang/include/clang/Sema/ExternalSemaSource.h b/clang/include/clang/Sema/ExternalSemaSource.h index 22d1ee2df115a6..8b41c5483458a0 100644 --- a/clang/include/clang/Sema/ExternalSemaSource.h +++ b/clang/include/clang/Sema/ExternalSemaSource.h @@ -181,6 +181,9 @@ class ExternalSemaSource : public ExternalASTSource { SmallVectorImpl > &Pending) {} + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector &Decls){}; + /// Read the set of late parsed template functions for this source. /// /// The external source should insert its own late parsed template functions diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h index 2bf91cb5212c5e..6054ef39e54ff9 100644 --- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h +++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h @@ -319,6 +319,9 @@ class MultiplexExternalSemaSource : public ExternalSemaSource { void ReadPendingInstantiations( SmallVectorImpl >& Pending) override; + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector &Decls) override; + /// Read the set of late parsed template functions for this source. /// /// The external source should insert its own late parsed template functions diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index e75a8bdb1fc72f..6de1a098e067a3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -59,6 +59,7 @@ #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -10087,6 +10088,12 @@ class Sema final { /// but have not yet been performed. std::deque PendingInstantiations; + /// Track constexpr functions referenced before they are (lexically) defined. + /// The key is the pattern, associated with a list of specialisations that + /// need to be instantiated when the pattern is defined. + llvm::DenseMap> + PendingInstantiationsOfConstexprEntities; + /// Queue of implicit template instantiations that cannot be performed /// eagerly. SmallVector LateParsedInstantiations; @@ -10405,6 +10412,9 @@ class Sema final { bool Recursive = false, bool DefinitionRequired = false, bool AtEndOfTU = false); + + void PerformPendingInstantiationsOfConstexprFunctions(FunctionDecl *Template); + VarTemplateSpecializationDecl *BuildVarTemplateInstantiation( VarTemplateDecl *VarTemplate, VarDecl *FromVar, const TemplateArgumentList &TemplateArgList, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index fdd64f2abbe937..08642889b0cf3e 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -695,6 +695,10 @@ enum ASTRecordTypes { /// Record code for an unterminated \#pragma clang assume_nonnull begin /// recorded in a preamble. PP_ASSUME_NONNULL_LOC = 67, + + /// Record code for constexpr templated entities that have been used but not + /// yet instantiated. + PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES = 68, }; /// Record types used within a source manager block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 7eefdca6815cda..407fc614f483e6 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -814,6 +814,9 @@ class ASTReader /// is the instantiation location. SmallVector PendingInstantiations; + llvm::DenseMap> + PendingInstantiationsOfConstexprEntities; + //@} /// \name DiagnosticsEngine-relevant special data @@ -2101,6 +2104,9 @@ class ASTReader SmallVectorImpl> &Pending) override; + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector &Decls) override; + void ReadLateParsedTemplates( llvm::MapVector> &LPTMap) override; diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp index 058e22cb2b814e..d0d6a3a866d62d 100644 --- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp +++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp @@ -310,6 +310,12 @@ void MultiplexExternalSemaSource::ReadPendingInstantiations( Sources[i]->ReadPendingInstantiations(Pending); } +void MultiplexExternalSemaSource::ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector &Decls) { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadPendingInstantiationsOfConstexprEntity(D, Decls); +}; + void MultiplexExternalSemaSource::ReadLateParsedTemplates( llvm::MapVector> &LPTMap) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 77ff4e1df8e9c9..9591f8b87ba545 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16275,6 +16275,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FD && !FD->isDeleted()) checkTypeSupport(FD->getType(), FD->getLocation(), FD); + if (FD && FD->isConstexpr() && FD->isTemplated()) + PerformPendingInstantiationsOfConstexprFunctions(FD); + return dcl; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d1b2b8084b8ffe..b204cb01a0deff 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19053,12 +19053,17 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, CodeSynthesisContexts.size()) PendingLocalImplicitInstantiations.push_back( std::make_pair(Func, PointOfInstantiation)); - else if (Func->isConstexpr()) + 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 { + if (!Func->isDefined()) { + PendingInstantiationsOfConstexprEntities + [Func->getTemplateInstantiationPattern()->getCanonicalDecl()] + .push_back(Func); + } + } else { Func->setInstantiationIsPending(true); PendingInstantiations.push_back( std::make_pair(Func, PointOfInstantiation)); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index d768bb72e07c09..aa367e0083e6db 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6495,6 +6495,34 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) { PendingInstantiations.swap(delayedPCHInstantiations); } +// Instantiate all referenced specializations of the given function template +// definition. This make sure that constexpr function templates that are defined +// after the point of instantiation of their use can be evaluated after they +// are defined. see CWG2497. +void Sema::PerformPendingInstantiationsOfConstexprFunctions(FunctionDecl *Tpl) { + + auto InstantiateAll = [&](const auto &Range) { + for (NamedDecl *D : Range) { + FunctionDecl *Fun = cast(D); + InstantiateFunctionDefinition(Fun->getPointOfInstantiation(), Fun); + } + }; + + auto It = + PendingInstantiationsOfConstexprEntities.find(Tpl->getCanonicalDecl()); + if (It != PendingInstantiationsOfConstexprEntities.end()) { + auto Decls = std::move(It->second); + PendingInstantiationsOfConstexprEntities.erase(It); + InstantiateAll(Decls); + } + + llvm::SmallSetVector Decls; + if (ExternalSource) { + ExternalSource->ReadPendingInstantiationsOfConstexprEntity(Tpl, Decls); + InstantiateAll(Decls); + } +} + void Sema::PerformDependentDiagnostics(const DeclContext *Pattern, const MultiLevelTemplateArgumentList &TemplateArgs) { for (auto *DD : Pattern->ddiags()) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index f22da838424b41..ef191c23650752 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3709,6 +3709,19 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, } break; + case PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES: + if (Record.size() % 2 != 0) + return llvm::createStringError( + std::errc::illegal_byte_sequence, + "Invalid PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES block"); + + for (unsigned I = 0, N = Record.size(); I != N; /* in loop */) { + DeclID Key = getGlobalDeclID(F, Record[I++]); + DeclID Value = getGlobalDeclID(F, Record[I++]); + PendingInstantiationsOfConstexprEntities[Key].insert(Value); + } + break; + case SEMA_DECL_REFS: if (Record.size() != 3) return llvm::createStringError(std::errc::illegal_byte_sequence, @@ -8718,6 +8731,20 @@ void ASTReader::ReadPendingInstantiations( PendingInstantiations.clear(); } +void ASTReader::ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector &Decls) { + for (auto *Redecl : D->redecls()) { + if (!Redecl->isFromASTFile()) + continue; + DeclID Id = Redecl->getGlobalID(); + auto It = PendingInstantiationsOfConstexprEntities.find(Id); + if (It == PendingInstantiationsOfConstexprEntities.end()) + continue; + for (DeclID InstantiationId : It->second) + Decls.insert(cast(GetDecl(InstantiationId))); + } +} + void ASTReader::ReadLateParsedTemplates( llvm::MapVector> &LPTMap) { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 6df815234e235f..8c8048fce026ef 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -849,6 +849,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(SEMA_DECL_REFS); RECORD(WEAK_UNDECLARED_IDENTIFIERS); RECORD(PENDING_IMPLICIT_INSTANTIATIONS); + RECORD(PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES); RECORD(UPDATE_VISIBLE); RECORD(DECL_UPDATE_OFFSETS); RECORD(DECL_UPDATES); @@ -4836,6 +4837,16 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, assert(SemaRef.PendingLocalImplicitInstantiations.empty() && "There are local ones at end of translation unit!"); + // Build a record containing all pending instantiations of constexpr + // entities. + RecordData PendingInstantiationsOfConstexprEntities; + for (const auto &I : SemaRef.PendingInstantiationsOfConstexprEntities) { + for (const auto &Elem : I.second) { + AddDeclRef(I.first, PendingInstantiationsOfConstexprEntities); + AddDeclRef(Elem, PendingInstantiationsOfConstexprEntities); + } + } + // Build a record containing some declaration references. RecordData SemaDeclRefs; if (SemaRef.StdNamespace || SemaRef.StdBadAlloc || SemaRef.StdAlignValT) { @@ -5153,6 +5164,11 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, if (!PendingInstantiations.empty()) Stream.EmitRecord(PENDING_IMPLICIT_INSTANTIATIONS, PendingInstantiations); + // Write the record containing pending instantiations of constexpr entities. + if (!PendingInstantiationsOfConstexprEntities.empty()) + Stream.EmitRecord(PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES, + PendingInstantiationsOfConstexprEntities); + // Write the record containing declaration references of Sema. if (!SemaDeclRefs.empty()) Stream.EmitRecord(SEMA_DECL_REFS, SemaDeclRefs); diff --git a/clang/test/PCH/instantiate-used-constexpr-function.cpp b/clang/test/PCH/instantiate-used-constexpr-function.cpp new file mode 100644 index 00000000000000..3930d0467d866b --- /dev/null +++ b/clang/test/PCH/instantiate-used-constexpr-function.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t +// RUN: %clang_cc1 -std=c++2a -include-pch %t -verify %s + +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +template constexpr T f(); +constexpr int g() { return f(); } // #1 + +#else /*included pch*/ + +template constexpr T f() { return 123; } +int k[g()]; + +#endif // HEADER diff --git a/clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp b/clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp new file mode 100644 index 00000000000000..61a7fb01376805 --- /dev/null +++ b/clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// expected-no-diagnostics + +namespace GH73232 { + +template +struct basic_string { + constexpr void _M_construct(); + constexpr basic_string() { + _M_construct(); + } +}; + +basic_string a; + +template +constexpr void basic_string<_CharT>::_M_construct(){} +constexpr basic_string str{}; + +template +constexpr void g(T); + +constexpr int f() { g(0); return 0; } + +template +constexpr void g(T) {} + +constexpr int z = f(); + +}