Skip to content

Commit c1f3c40

Browse files
committed
[clang] Track function template instantiation from definition
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 #55509
1 parent 35defdf commit c1f3c40

File tree

11 files changed

+154
-28
lines changed

11 files changed

+154
-28
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ Bug Fixes to Attribute Support
121121
Bug Fixes to C++ Support
122122
^^^^^^^^^^^^^^^^^^^^^^^^
123123

124+
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
125+
124126
Bug Fixes to AST Handling
125127
^^^^^^^^^^^^^^^^^^^^^^^^^
126128

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
@@ -1777,6 +1777,8 @@ class DeclContext {
17771777
uint64_t HasImplicitReturnZero : 1;
17781778
LLVM_PREFERRED_TYPE(bool)
17791779
uint64_t IsLateTemplateParsed : 1;
1780+
LLVM_PREFERRED_TYPE(bool)
1781+
uint64_t IsInstantiatedFromMemberTemplate : 1;
17801782

17811783
/// Kind of contexpr specifier as defined by ConstexprSpecKind.
17821784
LLVM_PREFERRED_TYPE(ConstexprSpecKind)
@@ -1827,7 +1829,7 @@ class DeclContext {
18271829
};
18281830

18291831
/// Number of inherited and non-inherited bits in FunctionDeclBitfields.
1830-
enum { NumFunctionDeclBits = NumDeclContextBits + 31 };
1832+
enum { NumFunctionDeclBits = NumDeclContextBits + 32 };
18311833

18321834
/// Stores the bits used by CXXConstructorDecl. If modified
18331835
/// NumCXXConstructorDeclBits and the accessor
@@ -1838,12 +1840,12 @@ class DeclContext {
18381840
LLVM_PREFERRED_TYPE(FunctionDeclBitfields)
18391841
uint64_t : NumFunctionDeclBits;
18401842

1841-
/// 20 bits to fit in the remaining available space.
1843+
/// 19 bits to fit in the remaining available space.
18421844
/// Note that this makes CXXConstructorDeclBitfields take
18431845
/// exactly 64 bits and thus the width of NumCtorInitializers
18441846
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
18451847
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
1846-
uint64_t NumCtorInitializers : 17;
1848+
uint64_t NumCtorInitializers : 16;
18471849
LLVM_PREFERRED_TYPE(bool)
18481850
uint64_t IsInheritingConstructor : 1;
18491851

@@ -1857,7 +1859,7 @@ class DeclContext {
18571859
};
18581860

18591861
/// Number of inherited and non-inherited bits in CXXConstructorDeclBitfields.
1860-
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 20 };
1862+
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 19 };
18611863

18621864
/// Stores the bits used by ObjCMethodDecl.
18631865
/// If modified NumObjCMethodDeclBits and the accessor

clang/include/clang/AST/DeclTemplate.h

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

1014+
bool isCompatibleWithDefinition() const {
1015+
return getTemplatedDecl()->isInstantiatedFromMemberTemplate() ||
1016+
isThisDeclarationADefinition();
1017+
}
1018+
void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *D) {
1019+
getTemplatedDecl()->setInstantiatedFromMemberTemplate();
1020+
RedeclarableTemplateDecl::setInstantiatedFromMemberTemplate(D);
1021+
}
1022+
10141023
/// Return the specialization with the provided arguments if it exists,
10151024
/// otherwise return the insertion point.
10161025
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
@@ -4074,22 +4074,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40744074
if (FunctionTemplate->getFriendObjectKind())
40754075
Owner = FunctionTemplate->getLexicalDeclContext();
40764076
FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
4077-
// additional check for inline friend,
4078-
// ```
4079-
// template <class F1> int foo(F1 X);
4080-
// template <int A1> struct A {
4081-
// template <class F1> friend int foo(F1 X) { return A1; }
4082-
// };
4083-
// template struct A<1>;
4084-
// int a = foo(1.0);
4085-
// ```
4086-
const FunctionDecl *FDFriend;
4087-
if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None &&
4088-
FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) &&
4089-
FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) {
4090-
FD = const_cast<FunctionDecl *>(FDFriend);
4091-
Owner = FD->getLexicalDeclContext();
4092-
}
4077+
40934078
MultiLevelTemplateArgumentList SubstArgs(
40944079
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
40954080
/*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: 20 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,26 @@ 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+
DC = (*It)->getLexicalDeclContext();
5265+
Innermost.emplace(Function->getTemplateSpecializationArgs()->asArray());
5266+
}
52485267
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
5249-
Function, Function->getLexicalDeclContext(), /*Final=*/false,
5250-
/*Innermost=*/std::nullopt, false, PatternDecl);
5268+
Function, DC, /*Final=*/false, Innermost, false, PatternDecl);
52515269

52525270
// Substitute into the qualifier; we can get a substitution failure here
52535271
// 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: 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)