Skip to content

Commit b665902

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 Relanding patch, with no changes, after it was reverted due to revert of commit this patch depended on.
1 parent 53c9553 commit b665902

File tree

12 files changed

+169
-26
lines changed

12 files changed

+169
-26
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ Bug Fixes to C++ Support
495495
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
496496
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
497497
- Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
498+
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
498499
- Fixed an issue deducing non-type template arguments of reference type. (#GH73460)
499500
- Fixed an issue in constraint evaluation, where type constraints on the lambda expression
500501
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 isInstantiatedFromMemberTemplate() const {
2303+
return FunctionDeclBits.IsInstantiatedFromMemberTemplate;
2304+
}
2305+
void setInstantiatedFromMemberTemplate(bool Val = true) {
2306+
FunctionDeclBits.IsInstantiatedFromMemberTemplate = 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: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,8 @@ class DeclContext {
17661766
uint64_t HasImplicitReturnZero : 1;
17671767
LLVM_PREFERRED_TYPE(bool)
17681768
uint64_t IsLateTemplateParsed : 1;
1769+
LLVM_PREFERRED_TYPE(bool)
1770+
uint64_t IsInstantiatedFromMemberTemplate : 1;
17691771

17701772
/// Kind of contexpr specifier as defined by ConstexprSpecKind.
17711773
LLVM_PREFERRED_TYPE(ConstexprSpecKind)
@@ -1816,7 +1818,7 @@ class DeclContext {
18161818
};
18171819

18181820
/// Number of inherited and non-inherited bits in FunctionDeclBitfields.
1819-
enum { NumFunctionDeclBits = NumDeclContextBits + 31 };
1821+
enum { NumFunctionDeclBits = NumDeclContextBits + 32 };
18201822

18211823
/// Stores the bits used by CXXConstructorDecl. If modified
18221824
/// NumCXXConstructorDeclBits and the accessor
@@ -1827,12 +1829,12 @@ class DeclContext {
18271829
LLVM_PREFERRED_TYPE(FunctionDeclBitfields)
18281830
uint64_t : NumFunctionDeclBits;
18291831

1830-
/// 20 bits to fit in the remaining available space.
1832+
/// 19 bits to fit in the remaining available space.
18311833
/// Note that this makes CXXConstructorDeclBitfields take
18321834
/// exactly 64 bits and thus the width of NumCtorInitializers
18331835
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
18341836
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
1835-
uint64_t NumCtorInitializers : 17;
1837+
uint64_t NumCtorInitializers : 16;
18361838
LLVM_PREFERRED_TYPE(bool)
18371839
uint64_t IsInheritingConstructor : 1;
18381840

@@ -1846,7 +1848,7 @@ class DeclContext {
18461848
};
18471849

18481850
/// Number of inherited and non-inherited bits in CXXConstructorDeclBitfields.
1849-
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 20 };
1851+
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 19 };
18501852

18511853
/// Stores the bits used by ObjCMethodDecl.
18521854
/// 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
@@ -1018,6 +1018,15 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl {
10181018
return getTemplatedDecl()->isThisDeclarationADefinition();
10191019
}
10201020

1021+
bool isCompatibleWithDefinition() const {
1022+
return getTemplatedDecl()->isInstantiatedFromMemberTemplate() ||
1023+
isThisDeclarationADefinition();
1024+
}
1025+
void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *D) {
1026+
getTemplatedDecl()->setInstantiatedFromMemberTemplate();
1027+
RedeclarableTemplateDecl::setInstantiatedFromMemberTemplate(D);
1028+
}
1029+
10211030
/// Return the specialization with the provided arguments if it exists,
10221031
/// otherwise return the insertion point.
10231032
FunctionDecl *findSpecialization(ArrayRef<TemplateArgument> Args,

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13019,6 +13019,12 @@ class Sema final : public SemaBase {
1301913019
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
1302013020
bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);
1302113021

13022+
void getTemplateInstantiationArgs(
13023+
MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
13024+
const DeclContext *DC = nullptr, bool Final = false,
13025+
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
13026+
bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);
13027+
1302213028
/// RAII object to handle the state changes required to synthesize
1302313029
/// a function body.
1302413030
class SynthesizedFunctionScope {

clang/lib/AST/Decl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3066,6 +3066,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
30663066
FunctionDeclBits.IsIneligibleOrNotSelected = false;
30673067
FunctionDeclBits.HasImplicitReturnZero = false;
30683068
FunctionDeclBits.IsLateTemplateParsed = false;
3069+
FunctionDeclBits.IsInstantiatedFromMemberTemplate = false;
30693070
FunctionDeclBits.ConstexprKind = static_cast<uint64_t>(ConstexprKind);
30703071
FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false;
30713072
FunctionDeclBits.InstantiationIsPending = false;

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3928,22 +3928,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
39283928
if (FunctionTemplate->getFriendObjectKind())
39293929
Owner = FunctionTemplate->getLexicalDeclContext();
39303930
FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
3931-
// additional check for inline friend,
3932-
// ```
3933-
// template <class F1> int foo(F1 X);
3934-
// template <int A1> struct A {
3935-
// template <class F1> friend int foo(F1 X) { return A1; }
3936-
// };
3937-
// template struct A<1>;
3938-
// int a = foo(1.0);
3939-
// ```
3940-
const FunctionDecl *FDFriend;
3941-
if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None &&
3942-
FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) &&
3943-
FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) {
3944-
FD = const_cast<FunctionDecl *>(FDFriend);
3945-
Owner = FD->getLexicalDeclContext();
3946-
}
3931+
39473932
MultiLevelTemplateArgumentList SubstArgs(
39483933
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
39493934
/*Final=*/false);

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,13 +512,13 @@ struct TemplateInstantiationArgumentCollecter
512512

513513
} // namespace
514514

515-
MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
516-
const NamedDecl *ND, const DeclContext *DC, bool Final,
515+
void Sema::getTemplateInstantiationArgs(
516+
MultiLevelTemplateArgumentList &Result, const NamedDecl *ND,
517+
const DeclContext *DC, bool Final,
517518
std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary,
518519
bool ForConstraintInstantiation) {
519520
assert((ND || DC) && "Can't find arguments for a decl if one isn't provided");
520521
// Accumulate the set of template argument lists in this structure.
521-
MultiLevelTemplateArgumentList Result;
522522
const Decl *CurDecl = ND;
523523

524524
if (!CurDecl)
@@ -529,6 +529,17 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
529529
do {
530530
CurDecl = Collecter.Visit(const_cast<Decl *>(CurDecl));
531531
} while (CurDecl);
532+
}
533+
534+
MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
535+
const NamedDecl *ND, const DeclContext *DC, bool Final,
536+
std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary,
537+
bool ForConstraintInstantiation) {
538+
assert((ND || DC) && "Can't find arguments for a decl if one isn't provided");
539+
// Accumulate the set of template argument lists in this structure.
540+
MultiLevelTemplateArgumentList Result;
541+
getTemplateInstantiationArgs(Result, ND, DC, Final, Innermost,
542+
RelativeToPrimary, ForConstraintInstantiation);
532543
return Result;
533544
}
534545

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5227,8 +5227,26 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
52275227
RebuildTypeSourceInfoForDefaultSpecialMembers();
52285228
SetDeclDefaulted(Function, PatternDecl->getLocation());
52295229
} else {
5230-
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
5231-
Function, Function->getLexicalDeclContext());
5230+
DeclContext *DC = Function;
5231+
MultiLevelTemplateArgumentList TemplateArgs;
5232+
if (auto *Primary = Function->getPrimaryTemplate();
5233+
Primary &&
5234+
!isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
5235+
auto It = llvm::find_if(Primary->redecls(),
5236+
[](const RedeclarableTemplateDecl *RTD) {
5237+
return cast<FunctionTemplateDecl>(RTD)
5238+
->isCompatibleWithDefinition();
5239+
});
5240+
assert(It != Primary->redecls().end() &&
5241+
"Should't get here without a definition");
5242+
DC = (*It)->getLexicalDeclContext();
5243+
if (Function->getTemplateSpecializationKind() !=
5244+
TSK_ExplicitSpecialization)
5245+
TemplateArgs.addOuterTemplateArguments(
5246+
Function, Function->getTemplateSpecializationArgs()->asArray(),
5247+
/*Final=*/false);
5248+
}
5249+
getTemplateInstantiationArgs(TemplateArgs, /*D=*/nullptr, DC);
52325250

52335251
// Substitute into the qualifier; we can get a substitution failure here
52345252
// 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->setInstantiatedFromMemberTemplate(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->isInstantiatedFromMemberTemplate());
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)