Author: cor3ntin Date: 2023-11-30T08:45:05+01:00 New Revision: 030047c432cac133738be68fa0974f70e69dd58d
URL: https://github.com/llvm/llvm-project/commit/030047c432cac133738be68fa0974f70e69dd58d DIFF: https://github.com/llvm/llvm-project/commit/030047c432cac133738be68fa0974f70e69dd58d.diff LOG: [Clang] Eagerly instantiate used constexpr function upon definition. (#73463) Despite CWG2497 not being resolved, it is reasonable to expect the following code to compile (and which is supported by other compilers) ```cpp template<typename T> constexpr T f(); constexpr int g() { return f<int>(); } // #1 template<typename T> constexpr T f() { return 123; } int k[g()]; // #2 ``` To that end, we eagerly instantiate all referenced specializations of constexpr functions when they are defined. We maintain a map of (pattern, [instantiations]) independent of `PendingInstantiations` to avoid having to iterate that list after each function definition. We should apply the same logic to constexpr variables, but I wanted to keep the PR small. Fixes #73232 Added: clang/test/PCH/instantiate-used-constexpr-function.cpp clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Sema/ExternalSemaSource.h clang/include/clang/Sema/MultiplexExternalSemaSource.h clang/include/clang/Sema/Sema.h clang/include/clang/Serialization/ASTBitCodes.h clang/include/clang/Serialization/ASTReader.h clang/lib/Sema/MultiplexExternalSemaSource.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Serialization/ASTReader.cpp clang/lib/Serialization/ASTWriter.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8d2b60dd75acfee..7d64647433d92a7 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 <https://github.com/llvm/llvm-project/issues/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 <https://github.com/llvm/llvm-project/issues/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 22d1ee2df115a6e..8b41c5483458a0f 100644 --- a/clang/include/clang/Sema/ExternalSemaSource.h +++ b/clang/include/clang/Sema/ExternalSemaSource.h @@ -181,6 +181,9 @@ class ExternalSemaSource : public ExternalASTSource { SmallVectorImpl<std::pair<ValueDecl *, SourceLocation> > &Pending) {} + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector<NamedDecl *, 4> &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 2bf91cb5212c5eb..6054ef39e54ff9a 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<std::pair<ValueDecl*, SourceLocation> >& Pending) override; + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector<NamedDecl *, 4> &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 e75a8bdb1fc72ff..6de1a098e067a38 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<PendingImplicitInstantiation> 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<NamedDecl *, SmallVector<NamedDecl *>> + PendingInstantiationsOfConstexprEntities; + /// Queue of implicit template instantiations that cannot be performed /// eagerly. SmallVector<PendingImplicitInstantiation, 1> 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 fdd64f2abbe9375..08642889b0cf3eb 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 7eefdca6815cdad..407fc614f483e6c 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<serialization::DeclID, 64> PendingInstantiations; + llvm::DenseMap<serialization::DeclID, std::set<serialization::DeclID>> + PendingInstantiationsOfConstexprEntities; + //@} /// \name DiagnosticsEngine-relevant special data @@ -2101,6 +2104,9 @@ class ASTReader SmallVectorImpl<std::pair<ValueDecl *, SourceLocation>> &Pending) override; + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector<NamedDecl *, 4> &Decls) override; + void ReadLateParsedTemplates( llvm::MapVector<const FunctionDecl *, std::unique_ptr<LateParsedTemplate>> &LPTMap) override; diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp index 058e22cb2b814e6..d0d6a3a866d62d2 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<NamedDecl *, 4> &Decls) { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadPendingInstantiationsOfConstexprEntity(D, Decls); +}; + void MultiplexExternalSemaSource::ReadLateParsedTemplates( llvm::MapVector<const FunctionDecl *, std::unique_ptr<LateParsedTemplate>> &LPTMap) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 77ff4e1df8e9c90..9591f8b87ba5456 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 d1b2b8084b8ffea..b204cb01a0deffd 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 d768bb72e07c09a..aa367e0083e6dbf 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<FunctionDecl>(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<NamedDecl *, 4> 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 f22da838424b415..ef191c23650752c 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<NamedDecl *, 4> &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<NamedDecl>(GetDecl(InstantiationId))); + } +} + void ASTReader::ReadLateParsedTemplates( llvm::MapVector<const FunctionDecl *, std::unique_ptr<LateParsedTemplate>> &LPTMap) { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 6df815234e235fb..8c8048fce026ef7 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 000000000000000..3930d0467d866be --- /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<typename T> constexpr T f(); +constexpr int g() { return f<int>(); } // #1 + +#else /*included pch*/ + +template<typename T> 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 000000000000000..61a7fb01376805f --- /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 <typename _CharT> +struct basic_string { + constexpr void _M_construct(); + constexpr basic_string() { + _M_construct(); + } +}; + +basic_string<char> a; + +template <typename _CharT> +constexpr void basic_string<_CharT>::_M_construct(){} +constexpr basic_string<char> str{}; + +template <typename T> +constexpr void g(T); + +constexpr int f() { g(0); return 0; } + +template <typename T> +constexpr void g(T) {} + +constexpr int z = f(); + +} _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits