Skip to content

Commit 0ac3d1a

Browse files
committed
[clang] Track function template instantiation from definition
This fixes instantiation of definition for fend function templates, when the declaration found and the one containing the definition have different template contexts. In these cases, the the function declaration corresponding to the definition is not available; it may not even be instantiated at all. So this patch adds a bit which tracks which function template was instantiated from the definition. It's used to find which primary template serves as a context for the purpose of obtainining the template arguments needed to instantiate the definition. Fixes #55509
1 parent 9e862ae commit 0ac3d1a

File tree

11 files changed

+152
-28
lines changed

11 files changed

+152
-28
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ Bug Fixes to C++ Support
468468
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
469469
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
470470
- Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
471+
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
471472
- Fixed an issue deducing non-type template arguments of reference type. (#GH73460)
472473
- Fixed an issue in constraint evaluation, where type constraints on the lambda expression
473474
containing outer unexpanded parameters were not correctly expanded. (#GH101754)

clang/include/clang/AST/Decl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,6 +2299,13 @@ class FunctionDecl : public DeclaratorDecl,
22992299
FunctionDeclBits.IsLateTemplateParsed = ILT;
23002300
}
23012301

2302+
bool isInstantiatedFromDefinition() const {
2303+
return FunctionDeclBits.IsInstantiatedFromDefinition;
2304+
}
2305+
void setInstantiatedFromDefinition(bool Val = true) {
2306+
FunctionDeclBits.IsInstantiatedFromDefinition = Val;
2307+
}
2308+
23022309
/// Whether this function is "trivial" in some specialized C++ senses.
23032310
/// Can only be true for default constructors, copy constructors,
23042311
/// copy assignment operators, and destructors. Not meaningful until

clang/include/clang/AST/DeclBase.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,6 +1763,8 @@ class DeclContext {
17631763
uint64_t HasImplicitReturnZero : 1;
17641764
LLVM_PREFERRED_TYPE(bool)
17651765
uint64_t IsLateTemplateParsed : 1;
1766+
LLVM_PREFERRED_TYPE(bool)
1767+
uint64_t IsInstantiatedFromDefinition : 1;
17661768

17671769
/// Kind of contexpr specifier as defined by ConstexprSpecKind.
17681770
LLVM_PREFERRED_TYPE(ConstexprSpecKind)
@@ -1813,7 +1815,7 @@ class DeclContext {
18131815
};
18141816

18151817
/// Number of inherited and non-inherited bits in FunctionDeclBitfields.
1816-
enum { NumFunctionDeclBits = NumDeclContextBits + 31 };
1818+
enum { NumFunctionDeclBits = NumDeclContextBits + 32 };
18171819

18181820
/// Stores the bits used by CXXConstructorDecl. If modified
18191821
/// NumCXXConstructorDeclBits and the accessor
@@ -1829,7 +1831,7 @@ class DeclContext {
18291831
/// exactly 64 bits and thus the width of NumCtorInitializers
18301832
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
18311833
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
1832-
uint64_t NumCtorInitializers : 17;
1834+
uint64_t NumCtorInitializers : 16;
18331835
LLVM_PREFERRED_TYPE(bool)
18341836
uint64_t IsInheritingConstructor : 1;
18351837

@@ -1843,7 +1845,7 @@ class DeclContext {
18431845
};
18441846

18451847
/// Number of inherited and non-inherited bits in CXXConstructorDeclBitfields.
1846-
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 20 };
1848+
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 19 };
18471849

18481850
/// Stores the bits used by ObjCMethodDecl.
18491851
/// If modified NumObjCMethodDeclBits and the accessor

clang/include/clang/AST/DeclTemplate.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,13 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl {
10101010
return getTemplatedDecl()->isThisDeclarationADefinition();
10111011
}
10121012

1013+
bool isInstantiatedFromDefinition() const {
1014+
return getTemplatedDecl()->isInstantiatedFromDefinition();
1015+
}
1016+
void setInstantiatedFromDefinition() {
1017+
getTemplatedDecl()->setInstantiatedFromDefinition();
1018+
}
1019+
10131020
/// Return the specialization with the provided arguments if it exists,
10141021
/// otherwise return the insertion point.
10151022
FunctionDecl *findSpecialization(ArrayRef<TemplateArgument> Args,

clang/lib/AST/Decl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3059,6 +3059,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
30593059
FunctionDeclBits.IsIneligibleOrNotSelected = false;
30603060
FunctionDeclBits.HasImplicitReturnZero = false;
30613061
FunctionDeclBits.IsLateTemplateParsed = false;
3062+
FunctionDeclBits.IsInstantiatedFromDefinition = false;
30623063
FunctionDeclBits.ConstexprKind = static_cast<uint64_t>(ConstexprKind);
30633064
FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false;
30643065
FunctionDeclBits.InstantiationIsPending = false;

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4034,22 +4034,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40344034
if (FunctionTemplate->getFriendObjectKind())
40354035
Owner = FunctionTemplate->getLexicalDeclContext();
40364036
FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
4037-
// additional check for inline friend,
4038-
// ```
4039-
// template <class F1> int foo(F1 X);
4040-
// template <int A1> struct A {
4041-
// template <class F1> friend int foo(F1 X) { return A1; }
4042-
// };
4043-
// template struct A<1>;
4044-
// int a = foo(1.0);
4045-
// ```
4046-
const FunctionDecl *FDFriend;
4047-
if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None &&
4048-
FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) &&
4049-
FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) {
4050-
FD = const_cast<FunctionDecl *>(FDFriend);
4051-
Owner = FD->getLexicalDeclContext();
4052-
}
4037+
40534038
MultiLevelTemplateArgumentList SubstArgs(
40544039
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
40554040
/*Final=*/false);

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,6 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
476476
using namespace TemplateInstArgsHelpers;
477477
const Decl *CurDecl = ND;
478478

479-
if (!CurDecl)
480-
CurDecl = Decl::castFromDeclContext(DC);
481-
482479
if (Innermost) {
483480
Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND), *Innermost,
484481
Final);
@@ -492,8 +489,10 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
492489
// has a depth of 0.
493490
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl))
494491
HandleDefaultTempArgIntoTempTempParam(TTP, Result);
495-
CurDecl = Response::UseNextDecl(CurDecl).NextDecl;
496-
}
492+
CurDecl = DC ? Decl::castFromDeclContext(DC)
493+
: Response::UseNextDecl(CurDecl).NextDecl;
494+
} else if (!CurDecl)
495+
CurDecl = Decl::castFromDeclContext(DC);
497496

498497
while (!CurDecl->isFileContextDecl()) {
499498
Response R;

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "TreeTransform.h"
1313
#include "clang/AST/ASTConsumer.h"
1414
#include "clang/AST/ASTContext.h"
15+
#include "clang/AST/ASTLambda.h"
1516
#include "clang/AST/ASTMutationListener.h"
1617
#include "clang/AST/DeclTemplate.h"
1718
#include "clang/AST/DeclVisitor.h"
@@ -1994,8 +1995,10 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
19941995
// Link the instantiation back to the pattern *unless* this is a
19951996
// non-definition friend declaration.
19961997
if (!InstTemplate->getInstantiatedFromMemberTemplate() &&
1997-
!(isFriend && !D->getTemplatedDecl()->isThisDeclarationADefinition()))
1998+
!(isFriend && !D->getTemplatedDecl()->isThisDeclarationADefinition())) {
1999+
InstTemplate->setInstantiatedFromDefinition();
19982000
InstTemplate->setInstantiatedFromMemberTemplate(D);
2001+
}
19992002

20002003
// Make declarations visible in the appropriate context.
20012004
if (!isFriend) {
@@ -2299,6 +2302,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
22992302
FunctionTemplate->setLexicalDeclContext(LexicalDC);
23002303

23012304
if (isFriend && D->isThisDeclarationADefinition()) {
2305+
FunctionTemplate->setInstantiatedFromDefinition();
23022306
FunctionTemplate->setInstantiatedFromMemberTemplate(
23032307
D->getDescribedFunctionTemplate());
23042308
}
@@ -5185,9 +5189,24 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
51855189
RebuildTypeSourceInfoForDefaultSpecialMembers();
51865190
SetDeclDefaulted(Function, PatternDecl->getLocation());
51875191
} else {
5192+
NamedDecl *ND = Function;
5193+
std::optional<ArrayRef<TemplateArgument>> Innermost;
5194+
if (auto *Primary = Function->getPrimaryTemplate();
5195+
Primary &&
5196+
!isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
5197+
ND = *llvm::find_if(Primary->redecls(),
5198+
[](const RedeclarableTemplateDecl *RTD) {
5199+
auto *FTD = cast<FunctionTemplateDecl>(RTD);
5200+
return FTD->isInstantiatedFromDefinition() ||
5201+
FTD->isThisDeclarationADefinition();
5202+
});
5203+
if (Function->getTemplateSpecializationKind() !=
5204+
TSK_ExplicitSpecialization)
5205+
Innermost.emplace(Function->getTemplateSpecializationArgs()->asArray());
5206+
}
51885207
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
5189-
Function, Function->getLexicalDeclContext(), /*Final=*/false,
5190-
/*Innermost=*/std::nullopt, false, PatternDecl);
5208+
Function, ND->getLexicalDeclContext(), /*Final=*/false, Innermost,
5209+
false, PatternDecl);
51915210

51925211
// Substitute into the qualifier; we can get a substitution failure here
51935212
// through evil use of alias templates.

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
10871087
FD->setHasImplicitReturnZero(FunctionDeclBits.getNextBit());
10881088
FD->setIsMultiVersion(FunctionDeclBits.getNextBit());
10891089
FD->setLateTemplateParsed(FunctionDeclBits.getNextBit());
1090+
FD->setInstantiatedFromDefinition(FunctionDeclBits.getNextBit());
10901091
FD->setFriendConstraintRefersToEnclosingTemplate(
10911092
FunctionDeclBits.getNextBit());
10921093
FD->setUsesSEHTry(FunctionDeclBits.getNextBit());

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ void ASTDeclWriter::VisitDeclaratorDecl(DeclaratorDecl *D) {
626626
}
627627

628628
void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
629-
static_assert(DeclContext::NumFunctionDeclBits == 44,
629+
static_assert(DeclContext::NumFunctionDeclBits == 45,
630630
"You need to update the serializer after you change the "
631631
"FunctionDeclBits");
632632

@@ -732,6 +732,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
732732
FunctionDeclBits.addBit(D->hasImplicitReturnZero());
733733
FunctionDeclBits.addBit(D->isMultiVersion());
734734
FunctionDeclBits.addBit(D->isLateTemplateParsed());
735+
FunctionDeclBits.addBit(D->isInstantiatedFromDefinition());
735736
FunctionDeclBits.addBit(D->FriendConstraintRefersToEnclosingTemplate());
736737
FunctionDeclBits.addBit(D->usesSEHTry());
737738
Record.push_back(FunctionDeclBits);

clang/test/SemaTemplate/GH55509.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++26 %s
2+
3+
namespace t1 {
4+
template<int N> struct A {
5+
template<class C> friend auto cica(const A<N-1>&, C) {
6+
return N;
7+
}
8+
};
9+
10+
template<> struct A<0> {
11+
template<class C> friend auto cica(const A<0>&, C);
12+
// expected-note@-1 {{declared here}}
13+
};
14+
15+
void test() {
16+
cica(A<0>{}, 0);
17+
// expected-error@-1 {{function 'cica<int>' with deduced return type cannot be used before it is defined}}
18+
19+
(void)A<1>{};
20+
cica(A<0>{}, 0);
21+
}
22+
} // namespace t1
23+
namespace t2 {
24+
template<int N> struct A {
25+
template<class C> friend auto cica(const A<N-1>&, C) {
26+
return N;
27+
}
28+
};
29+
30+
template<> struct A<0> {
31+
template<class C> friend auto cica(const A<0>&, C);
32+
};
33+
34+
template <int N, class = decltype(cica(A<N>{}, nullptr))>
35+
void MakeCica();
36+
// expected-note@-1 {{candidate function}}
37+
38+
template <int N> void MakeCica(A<N+1> = {});
39+
// expected-note@-1 {{candidate function}}
40+
41+
void test() {
42+
MakeCica<0>();
43+
44+
MakeCica<0>();
45+
// expected-error@-1 {{call to 'MakeCica' is ambiguous}}
46+
}
47+
} // namespace t2
48+
namespace t3 {
49+
template<int N> struct A {
50+
template<class C> friend auto cica(const A<N-1>&, C) {
51+
return N-1;
52+
}
53+
};
54+
55+
template<> struct A<0> {
56+
template<class C> friend auto cica(const A<0>&, C);
57+
};
58+
59+
template <int N, class AT, class = decltype(cica(AT{}, nullptr))>
60+
static constexpr bool MakeCica(int);
61+
62+
template <int N, class AT>
63+
static constexpr bool MakeCica(short, A<N+1> = {});
64+
65+
template <int N, class AT = A<N>, class Val = decltype(MakeCica<N, AT>(0))>
66+
static constexpr bool has_cica = Val{};
67+
68+
constexpr bool cica2 = has_cica<0> || has_cica<0>;
69+
} // namespace t3
70+
namespace t4 {
71+
template<int N> struct A {
72+
template<class C> friend auto cica(const A<N-1>&, C);
73+
};
74+
75+
template<> struct A<0> {
76+
template<class C> friend auto cica(const A<0>&, C) {
77+
C a;
78+
}
79+
};
80+
81+
template struct A<1>;
82+
83+
void test() {
84+
cica(A<0>{}, 0);
85+
}
86+
} // namespace t4
87+
namespace regression1 {
88+
template <class> class A;
89+
90+
template <class T> [[gnu::abi_tag("TAG")]] void foo(A<T>);
91+
92+
template <class> struct A {
93+
friend void foo <>(A);
94+
};
95+
96+
template struct A<int>;
97+
98+
template <class T> [[gnu::abi_tag("TAG")]] void foo(A<T>) {}
99+
100+
template void foo<int>(A<int>);
101+
} // namespace regression1

0 commit comments

Comments
 (0)