diff options
author | cor3ntin <corentinjabot@gmail.com> | 2023-11-30 08:45:05 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-30 08:45:05 +0100 |
commit | 030047c432cac133738be68fa0974f70e69dd58d (patch) | |
tree | 9e0e500d92fd0a6f05ab250c840fe2dedb0b5574 /clang/lib | |
parent | 8a66510fa73c1507c2a58338e180ddb075993a5a (diff) | |
download | llvm-030047c432cac133738be68fa0974f70e69dd58d.zip llvm-030047c432cac133738be68fa0974f70e69dd58d.tar.gz llvm-030047c432cac133738be68fa0974f70e69dd58d.tar.bz2 |
[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
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Sema/MultiplexExternalSemaSource.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 9 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 28 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 27 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriter.cpp | 16 |
6 files changed, 87 insertions, 2 deletions
diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp index 058e22c..d0d6a3a 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 77ff4e1..9591f8b 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 d1b2b80..b204cb0 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 d768bb7..aa367e0 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 f22da83..ef191c2 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 6df8152..8c8048f 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); |