mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 10:06:05 +00:00
[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
This commit is contained in:
parent
8a66510fa7
commit
030047c432
@ -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.
|
||||
|
@ -181,6 +181,9 @@ public:
|
||||
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
|
||||
|
@ -319,6 +319,9 @@ public:
|
||||
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
|
||||
|
@ -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 @@ public:
|
||||
/// 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 @@ public:
|
||||
bool Recursive = false,
|
||||
bool DefinitionRequired = false,
|
||||
bool AtEndOfTU = false);
|
||||
|
||||
void PerformPendingInstantiationsOfConstexprFunctions(FunctionDecl *Template);
|
||||
|
||||
VarTemplateSpecializationDecl *BuildVarTemplateInstantiation(
|
||||
VarTemplateDecl *VarTemplate, VarDecl *FromVar,
|
||||
const TemplateArgumentList &TemplateArgList,
|
||||
|
@ -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.
|
||||
|
@ -814,6 +814,9 @@ private:
|
||||
/// 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 @@ public:
|
||||
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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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()) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
17
clang/test/PCH/instantiate-used-constexpr-function.cpp
Normal file
17
clang/test/PCH/instantiate-used-constexpr-function.cpp
Normal file
@ -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
|
@ -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();
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user