Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang] Eagerly instantiate used constexpr function upon definition. #73463

Merged
merged 4 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/ExternalSemaSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/MultiplexExternalSemaSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/MultiplexExternalSemaSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
9 changes: 7 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
27 changes: 27 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
zygoloid marked this conversation as resolved.
Show resolved Hide resolved
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) {
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case this will be relanded in some form:

This loop uses arbitrary hashtable order, leading to nondeterministic PCM output.

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) {
Expand Down Expand Up @@ -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);
Expand Down
17 changes: 17 additions & 0 deletions clang/test/PCH/instantiate-used-constexpr-function.cpp
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp
Original file line number Diff line number Diff line change
@@ -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();

}