Skip to content

Commit 4336f00

Browse files
authored
[clang] Track function template instantiation from definition (llvm#110387)
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 obtaining the template arguments needed to instantiate the definition. Fixes llvm#55509
1 parent d50302f commit 4336f00

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
@@ -474,6 +474,7 @@ Bug Fixes to C++ Support
474474
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
475475
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
476476
- Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
477+
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
477478
- Fixed an issue deducing non-type template arguments of reference type. (#GH73460)
478479
- Fixed an issue in constraint evaluation, where type constraints on the lambda expression
479480
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
@@ -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 IsInstantiatedFromMemberTemplate : 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
@@ -1824,12 +1826,12 @@ class DeclContext {
18241826
LLVM_PREFERRED_TYPE(FunctionDeclBitfields)
18251827
uint64_t : NumFunctionDeclBits;
18261828

1827-
/// 20 bits to fit in the remaining available space.
1829+
/// 19 bits to fit in the remaining available space.
18281830
/// Note that this makes CXXConstructorDeclBitfields take
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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,15 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl {
10081008
return getTemplatedDecl()->isThisDeclarationADefinition();
10091009
}
10101010

1011+
bool isCompatibleWithDefinition() const {
1012+
return getTemplatedDecl()->isInstantiatedFromMemberTemplate() ||
1013+
isThisDeclarationADefinition();
1014+
}
1015+
void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *D) {
1016+
getTemplatedDecl()->setInstantiatedFromMemberTemplate();
1017+
RedeclarableTemplateDecl::setInstantiatedFromMemberTemplate(D);
1018+
}
1019+
10111020
/// Return the specialization with the provided arguments if it exists,
10121021
/// otherwise return the insertion point.
10131022
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
@@ -13027,6 +13027,12 @@ class Sema final : public SemaBase {
1302713027
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
1302813028
bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);
1302913029

13030+
void getTemplateInstantiationArgs(
13031+
MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
13032+
const DeclContext *DC = nullptr, bool Final = false,
13033+
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
13034+
bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);
13035+
1303013036
/// RAII object to handle the state changes required to synthesize
1303113037
/// a function body.
1303213038
class SynthesizedFunctionScope {

clang/lib/AST/Decl.cpp

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

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4007,22 +4007,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
40074007
if (FunctionTemplate->getFriendObjectKind())
40084008
Owner = FunctionTemplate->getLexicalDeclContext();
40094009
FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
4010-
// additional check for inline friend,
4011-
// ```
4012-
// template <class F1> int foo(F1 X);
4013-
// template <int A1> struct A {
4014-
// template <class F1> friend int foo(F1 X) { return A1; }
4015-
// };
4016-
// template struct A<1>;
4017-
// int a = foo(1.0);
4018-
// ```
4019-
const FunctionDecl *FDFriend;
4020-
if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None &&
4021-
FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) &&
4022-
FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) {
4023-
FD = const_cast<FunctionDecl *>(FDFriend);
4024-
Owner = FD->getLexicalDeclContext();
4025-
}
4010+
40264011
MultiLevelTemplateArgumentList SubstArgs(
40274012
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
40284013
/*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
@@ -5214,8 +5214,26 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
52145214
RebuildTypeSourceInfoForDefaultSpecialMembers();
52155215
SetDeclDefaulted(Function, PatternDecl->getLocation());
52165216
} else {
5217-
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
5218-
Function, Function->getLexicalDeclContext());
5217+
DeclContext *DC = Function;
5218+
MultiLevelTemplateArgumentList TemplateArgs;
5219+
if (auto *Primary = Function->getPrimaryTemplate();
5220+
Primary &&
5221+
!isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
5222+
auto It = llvm::find_if(Primary->redecls(),
5223+
[](const RedeclarableTemplateDecl *RTD) {
5224+
return cast<FunctionTemplateDecl>(RTD)
5225+
->isCompatibleWithDefinition();
5226+
});
5227+
assert(It != Primary->redecls().end() &&
5228+
"Should't get here without a definition");
5229+
DC = (*It)->getLexicalDeclContext();
5230+
if (Function->getTemplateSpecializationKind() !=
5231+
TSK_ExplicitSpecialization)
5232+
TemplateArgs.addOuterTemplateArguments(
5233+
Function, Function->getTemplateSpecializationArgs()->asArray(),
5234+
/*Final=*/false);
5235+
}
5236+
getTemplateInstantiationArgs(TemplateArgs, /*D=*/nullptr, DC);
52195237

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