Skip to content

Commit 346077a

Browse files
authored
Reland: [clang] Track function template instantiation from definition (#125266)
1 parent f9dbf1a commit 346077a

File tree

11 files changed

+181
-28
lines changed

11 files changed

+181
-28
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ Bug Fixes to Attribute Support
145145
Bug Fixes to C++ Support
146146
^^^^^^^^^^^^^^^^^^^^^^^^
147147

148+
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
149+
148150
Bug Fixes to AST Handling
149151
^^^^^^^^^^^^^^^^^^^^^^^^^
150152

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
@@ -3065,6 +3065,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
30653065
FunctionDeclBits.IsIneligibleOrNotSelected = false;
30663066
FunctionDeclBits.HasImplicitReturnZero = false;
30673067
FunctionDeclBits.IsLateTemplateParsed = false;
3068+
FunctionDeclBits.IsInstantiatedFromMemberTemplate = false;
30683069
FunctionDeclBits.ConstexprKind = static_cast<uint64_t>(ConstexprKind);
30693070
FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false;
30703071
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"
@@ -5276,9 +5277,31 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
52765277
RebuildTypeSourceInfoForDefaultSpecialMembers();
52775278
SetDeclDefaulted(Function, PatternDecl->getLocation());
52785279
} else {
5280+
NamedDecl *ND = Function;
5281+
DeclContext *DC = ND->getLexicalDeclContext();
5282+
std::optional<ArrayRef<TemplateArgument>> Innermost;
5283+
if (auto *Primary = Function->getPrimaryTemplate();
5284+
Primary &&
5285+
!isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) &&
5286+
Function->getTemplateSpecializationKind() !=
5287+
TSK_ExplicitSpecialization) {
5288+
auto It = llvm::find_if(Primary->redecls(),
5289+
[](const RedeclarableTemplateDecl *RTD) {
5290+
return cast<FunctionTemplateDecl>(RTD)
5291+
->isCompatibleWithDefinition();
5292+
});
5293+
assert(It != Primary->redecls().end() &&
5294+
"Should't get here without a definition");
5295+
if (FunctionDecl *Def = cast<FunctionTemplateDecl>(*It)
5296+
->getTemplatedDecl()
5297+
->getDefinition())
5298+
DC = Def->getLexicalDeclContext();
5299+
else
5300+
DC = (*It)->getLexicalDeclContext();
5301+
Innermost.emplace(Function->getTemplateSpecializationArgs()->asArray());
5302+
}
52795303
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
5280-
Function, Function->getLexicalDeclContext(), /*Final=*/false,
5281-
/*Innermost=*/std::nullopt, false, PatternDecl);
5304+
Function, DC, /*Final=*/false, Innermost, false, PatternDecl);
52825305

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