Skip to content

Commit deb63e7

Browse files
authored
[clang] Track function template instantiation from definition (llvm#125266) (llvm#127777)
This fixes instantiation of definition for friend 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 declaration was instantiated from the member template. 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 llvm#55509
1 parent 720ab3b commit deb63e7

File tree

11 files changed

+180
-28
lines changed

11 files changed

+180
-28
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,7 @@ Bug Fixes to C++ Support
10571057
- Fix that some dependent immediate expressions did not cause immediate escalation (#GH119046)
10581058
- Fixed a substitution bug in transforming CTAD aliases when the type alias contains a non-pack template argument
10591059
corresponding to a pack parameter (#GH124715)
1060+
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
10601061

10611062
Bug Fixes to AST Handling
10621063
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/Decl.h

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

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

clang/include/clang/AST/DeclBase.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,6 +1780,8 @@ class DeclContext {
17801780
uint64_t HasImplicitReturnZero : 1;
17811781
LLVM_PREFERRED_TYPE(bool)
17821782
uint64_t IsLateTemplateParsed : 1;
1783+
LLVM_PREFERRED_TYPE(bool)
1784+
uint64_t IsInstantiatedFromMemberTemplate : 1;
17831785

17841786
/// Kind of contexpr specifier as defined by ConstexprSpecKind.
17851787
LLVM_PREFERRED_TYPE(ConstexprSpecKind)
@@ -1830,7 +1832,7 @@ class DeclContext {
18301832
};
18311833

18321834
/// Number of inherited and non-inherited bits in FunctionDeclBitfields.
1833-
enum { NumFunctionDeclBits = NumDeclContextBits + 31 };
1835+
enum { NumFunctionDeclBits = NumDeclContextBits + 32 };
18341836

18351837
/// Stores the bits used by CXXConstructorDecl. If modified
18361838
/// NumCXXConstructorDeclBits and the accessor
@@ -1841,12 +1843,12 @@ class DeclContext {
18411843
LLVM_PREFERRED_TYPE(FunctionDeclBitfields)
18421844
uint64_t : NumFunctionDeclBits;
18431845

1844-
/// 20 bits to fit in the remaining available space.
1846+
/// 19 bits to fit in the remaining available space.
18451847
/// Note that this makes CXXConstructorDeclBitfields take
18461848
/// exactly 64 bits and thus the width of NumCtorInitializers
18471849
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
18481850
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
1849-
uint64_t NumCtorInitializers : 17;
1851+
uint64_t NumCtorInitializers : 16;
18501852
LLVM_PREFERRED_TYPE(bool)
18511853
uint64_t IsInheritingConstructor : 1;
18521854

@@ -1860,7 +1862,7 @@ class DeclContext {
18601862
};
18611863

18621864
/// Number of inherited and non-inherited bits in CXXConstructorDeclBitfields.
1863-
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 20 };
1865+
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 19 };
18641866

18651867
/// Stores the bits used by ObjCMethodDecl.
18661868
/// If modified NumObjCMethodDeclBits and the accessor

clang/include/clang/AST/DeclTemplate.h

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

1014+
bool isCompatibleWithDefinition() const {
1015+
return getTemplatedDecl()->isInstantiatedFromMemberTemplate() ||
1016+
isThisDeclarationADefinition();
1017+
}
1018+
1019+
// This bit closely tracks 'RedeclarableTemplateDecl::InstantiatedFromMember',
1020+
// except this is per declaration, while the redeclarable field is
1021+
// per chain. This indicates a template redeclaration which
1022+
// is compatible with the definition, in the non-trivial case
1023+
// where this is not already a definition.
1024+
// This is only really needed for instantiating the definition of friend
1025+
// function templates, which can have redeclarations in different template
1026+
// contexts.
1027+
// The bit is actually stored in the FunctionDecl for space efficiency
1028+
// reasons.
1029+
void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *D) {
1030+
getTemplatedDecl()->setInstantiatedFromMemberTemplate();
1031+
RedeclarableTemplateDecl::setInstantiatedFromMemberTemplate(D);
1032+
}
1033+
10141034
/// Return the specialization with the provided arguments if it exists,
10151035
/// otherwise return the insertion point.
10161036
FunctionDecl *findSpecialization(ArrayRef<TemplateArgument> Args,

clang/lib/AST/Decl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3069,6 +3069,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
30693069
FunctionDeclBits.IsIneligibleOrNotSelected = false;
30703070
FunctionDeclBits.HasImplicitReturnZero = false;
30713071
FunctionDeclBits.IsLateTemplateParsed = false;
3072+
FunctionDeclBits.IsInstantiatedFromMemberTemplate = false;
30723073
FunctionDeclBits.ConstexprKind = static_cast<uint64_t>(ConstexprKind);
30733074
FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false;
30743075
FunctionDeclBits.InstantiationIsPending = false;

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4072,22 +4072,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40724072
if (FunctionTemplate->getFriendObjectKind())
40734073
Owner = FunctionTemplate->getLexicalDeclContext();
40744074
FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
4075-
// additional check for inline friend,
4076-
// ```
4077-
// template <class F1> int foo(F1 X);
4078-
// template <int A1> struct A {
4079-
// template <class F1> friend int foo(F1 X) { return A1; }
4080-
// };
4081-
// template struct A<1>;
4082-
// int a = foo(1.0);
4083-
// ```
4084-
const FunctionDecl *FDFriend;
4085-
if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None &&
4086-
FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) &&
4087-
FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) {
4088-
FD = const_cast<FunctionDecl *>(FDFriend);
4089-
Owner = FD->getLexicalDeclContext();
4090-
}
4075+
40914076
MultiLevelTemplateArgumentList SubstArgs(
40924077
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
40934078
/*Final=*/false);

clang/lib/Sema/SemaTemplateInstantiate.cpp

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

482-
if (!CurDecl)
483-
CurDecl = Decl::castFromDeclContext(DC);
484-
485482
if (Innermost) {
486483
Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND), *Innermost,
487484
Final);
@@ -495,8 +492,10 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
495492
// has a depth of 0.
496493
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl))
497494
HandleDefaultTempArgIntoTempTempParam(TTP, Result);
498-
CurDecl = Response::UseNextDecl(CurDecl).NextDecl;
499-
}
495+
CurDecl = DC ? Decl::castFromDeclContext(DC)
496+
: Response::UseNextDecl(CurDecl).NextDecl;
497+
} else if (!CurDecl)
498+
CurDecl = Decl::castFromDeclContext(DC);
500499

501500
while (!CurDecl->isFileContextDecl()) {
502501
Response R;

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 25 additions & 2 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/DependentDiagnostic.h"
@@ -5245,9 +5246,31 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
52455246
RebuildTypeSourceInfoForDefaultSpecialMembers();
52465247
SetDeclDefaulted(Function, PatternDecl->getLocation());
52475248
} else {
5249+
NamedDecl *ND = Function;
5250+
DeclContext *DC = ND->getLexicalDeclContext();
5251+
std::optional<ArrayRef<TemplateArgument>> Innermost;
5252+
if (auto *Primary = Function->getPrimaryTemplate();
5253+
Primary &&
5254+
!isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) &&
5255+
Function->getTemplateSpecializationKind() !=
5256+
TSK_ExplicitSpecialization) {
5257+
auto It = llvm::find_if(Primary->redecls(),
5258+
[](const RedeclarableTemplateDecl *RTD) {
5259+
return cast<FunctionTemplateDecl>(RTD)
5260+
->isCompatibleWithDefinition();
5261+
});
5262+
assert(It != Primary->redecls().end() &&
5263+
"Should't get here without a definition");
5264+
if (FunctionDecl *Def = cast<FunctionTemplateDecl>(*It)
5265+
->getTemplatedDecl()
5266+
->getDefinition())
5267+
DC = Def->getLexicalDeclContext();
5268+
else
5269+
DC = (*It)->getLexicalDeclContext();
5270+
Innermost.emplace(Function->getTemplateSpecializationArgs()->asArray());
5271+
}
52485272
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
5249-
Function, Function->getLexicalDeclContext(), /*Final=*/false,
5250-
/*Innermost=*/std::nullopt, false, PatternDecl);
5273+
Function, DC, /*Final=*/false, Innermost, false, PatternDecl);
52515274

52525275
// Substitute into the qualifier; we can get a substitution failure here
52535276
// 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
@@ -1064,6 +1064,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
10641064
FD->setHasImplicitReturnZero(FunctionDeclBits.getNextBit());
10651065
FD->setIsMultiVersion(FunctionDeclBits.getNextBit());
10661066
FD->setLateTemplateParsed(FunctionDeclBits.getNextBit());
1067+
FD->setInstantiatedFromMemberTemplate(FunctionDeclBits.getNextBit());
10671068
FD->setFriendConstraintRefersToEnclosingTemplate(
10681069
FunctionDeclBits.getNextBit());
10691070
FD->setUsesSEHTry(FunctionDeclBits.getNextBit());

clang/lib/Serialization/ASTWriterDecl.cpp

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

681681
void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
682-
static_assert(DeclContext::NumFunctionDeclBits == 44,
682+
static_assert(DeclContext::NumFunctionDeclBits == 45,
683683
"You need to update the serializer after you change the "
684684
"FunctionDeclBits");
685685

@@ -785,6 +785,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
785785
FunctionDeclBits.addBit(D->hasImplicitReturnZero());
786786
FunctionDeclBits.addBit(D->isMultiVersion());
787787
FunctionDeclBits.addBit(D->isLateTemplateParsed());
788+
FunctionDeclBits.addBit(D->isInstantiatedFromMemberTemplate());
788789
FunctionDeclBits.addBit(D->FriendConstraintRefersToEnclosingTemplate());
789790
FunctionDeclBits.addBit(D->usesSEHTry());
790791
Record.push_back(FunctionDeclBits);

clang/test/SemaTemplate/GH55509.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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
102+
namespace regression2 {
103+
template <class> struct A {
104+
template <class T> static void f() {
105+
A<int>::f<T>();
106+
}
107+
};
108+
template <> template <class T> void A<int>::f() {
109+
static_assert(__is_same(T, long));
110+
}
111+
template void A<void>::f<long>();
112+
} // namespace regression2

0 commit comments

Comments
 (0)