Skip to content

Commit af4176f

Browse files
committed
[Clang] Eagerly instantiate used constexpr function upon definition.
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]) independant 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
1 parent 586986a commit af4176f

File tree

7 files changed

+78
-5
lines changed

7 files changed

+78
-5
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,11 @@ Bug Fixes to C++ Support
793793
- Fix crash when parsing nested requirement. Fixes:
794794
(`#73112 <https://github.com/llvm/llvm-project/issues/73112>`_)
795795

796+
- Clang now immediately instantiates function template specializations
797+
at the end of the definition of the corresponding function template
798+
when the definition appears after the first point of instantiation.
799+
(`#73232 <https://github.com/llvm/llvm-project/issues/73232>`_)
800+
796801
Bug Fixes to AST Handling
797802
^^^^^^^^^^^^^^^^^^^^^^^^^
798803
- Fixed an import failure of recursive friend class template.

clang/include/clang/Sema/Sema.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "clang/Sema/TypoCorrection.h"
6060
#include "clang/Sema/Weak.h"
6161
#include "llvm/ADT/ArrayRef.h"
62+
#include "llvm/ADT/DenseMap.h"
6263
#include "llvm/ADT/SetVector.h"
6364
#include "llvm/ADT/SmallBitVector.h"
6465
#include "llvm/ADT/SmallPtrSet.h"
@@ -10087,6 +10088,12 @@ class Sema final {
1008710088
/// but have not yet been performed.
1008810089
std::deque<PendingImplicitInstantiation> PendingInstantiations;
1008910090

10091+
/// Track constexpr functions referenced before they are (lexically) defined.
10092+
/// The key is the pattern, associated with a list of specialisations that
10093+
/// need to be instantiated when the pattern is defined.
10094+
llvm::DenseMap<NamedDecl *, SmallVector<NamedDecl *>>
10095+
PendingInstantiationsOfConstexprEntities;
10096+
1009010097
/// Queue of implicit template instantiations that cannot be performed
1009110098
/// eagerly.
1009210099
SmallVector<PendingImplicitInstantiation, 1> LateParsedInstantiations;
@@ -10405,6 +10412,10 @@ class Sema final {
1040510412
bool Recursive = false,
1040610413
bool DefinitionRequired = false,
1040710414
bool AtEndOfTU = false);
10415+
10416+
void InstantiateFunctionTemplateSpecializations(
10417+
SourceLocation PointOfInstantiation, FunctionDecl *Template);
10418+
1040810419
VarTemplateSpecializationDecl *BuildVarTemplateInstantiation(
1040910420
VarTemplateDecl *VarTemplate, VarDecl *FromVar,
1041010421
const TemplateArgumentList &TemplateArgList,

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16275,6 +16275,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1627516275
if (FD && !FD->isDeleted())
1627616276
checkTypeSupport(FD->getType(), FD->getLocation(), FD);
1627716277

16278+
if (FD && FD->isConstexpr() && FD->isTemplated())
16279+
InstantiateFunctionTemplateSpecializations(FD->getEndLoc(), FD);
16280+
1627816281
return dcl;
1627916282
}
1628016283

clang/lib/Sema/SemaExpr.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19053,12 +19053,17 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
1905319053
CodeSynthesisContexts.size())
1905419054
PendingLocalImplicitInstantiations.push_back(
1905519055
std::make_pair(Func, PointOfInstantiation));
19056-
else if (Func->isConstexpr())
19056+
else if (Func->isConstexpr()) {
1905719057
// Do not defer instantiations of constexpr functions, to avoid the
1905819058
// expression evaluator needing to call back into Sema if it sees a
1905919059
// call to such a function.
1906019060
InstantiateFunctionDefinition(PointOfInstantiation, Func);
19061-
else {
19061+
if (!Func->isDefined()) {
19062+
PendingInstantiationsOfConstexprEntities
19063+
[Func->getTemplateInstantiationPattern()->getCanonicalDecl()]
19064+
.push_back(Func);
19065+
}
19066+
} else {
1906219067
Func->setInstantiationIsPending(true);
1906319068
PendingInstantiations.push_back(
1906419069
std::make_pair(Func, PointOfInstantiation));

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6495,6 +6495,23 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) {
64956495
PendingInstantiations.swap(delayedPCHInstantiations);
64966496
}
64976497

6498+
// Instantiate all referenced specializations of the given function template
6499+
// definition. This make sure that function template that are defined after the
6500+
// point of instantiation of their used can be evaluated after they are defined.
6501+
// see CWG2497.
6502+
void Sema::InstantiateFunctionTemplateSpecializations(
6503+
SourceLocation PointOfInstantiation, FunctionDecl *Tpl) {
6504+
auto It =
6505+
PendingInstantiationsOfConstexprEntities.find(Tpl->getCanonicalDecl());
6506+
if (It == PendingInstantiationsOfConstexprEntities.end())
6507+
return;
6508+
for (NamedDecl *Fun : It->second) {
6509+
InstantiateFunctionDefinition(PointOfInstantiation,
6510+
cast<FunctionDecl>(Fun));
6511+
}
6512+
PendingInstantiationsOfConstexprEntities.erase(It);
6513+
}
6514+
64986515
void Sema::PerformDependentDiagnostics(const DeclContext *Pattern,
64996516
const MultiLevelTemplateArgumentList &TemplateArgs) {
65006517
for (auto *DD : Pattern->ddiags()) {

clang/test/SemaCXX/cxx2b-consteval-propagate.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,15 @@ template <typename T>
105105
constexpr int f(T t);
106106

107107
auto a = &f<char>;
108-
auto b = &f<int>; // expected-error {{immediate function 'f<int>' used before it is defined}} \
109-
// expected-note {{in instantiation of function template specialization}}
108+
auto b = &f<int>; // expected-error {{immediate function 'f<int>' used before it is defined}}
110109

111110
template <typename T>
112111
constexpr int f(T t) { // expected-note {{'f<int>' defined here}}
113112
return id(t); // expected-note {{'f<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
114-
}
113+
} // expected-note {{in instantiation of function template specialization}}
114+
115+
116+
115117
}
116118

117119
namespace constructors {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
// expected-no-diagnostics
3+
4+
namespace GH73232 {
5+
6+
template <typename _CharT>
7+
struct basic_string {
8+
constexpr void _M_construct();
9+
constexpr basic_string() {
10+
_M_construct();
11+
}
12+
};
13+
14+
basic_string<char> a;
15+
16+
template <typename _CharT>
17+
constexpr void basic_string<_CharT>::_M_construct(){}
18+
constexpr basic_string<char> str{};
19+
20+
template <typename T>
21+
constexpr void g(T);
22+
23+
constexpr int f() { g(0); return 0; }
24+
25+
template <typename T>
26+
constexpr void g(T) {}
27+
28+
constexpr int z = f();
29+
30+
}

0 commit comments

Comments
 (0)