Skip to content

Commit 362444a

Browse files
committed
[clang] Redeclare function templates instances per primary template
This fixes handling of friend function templates instances when their template context changes, such as when a new friend declaration is introduced after an instance was already created from a previous declaration. Instead of producing one function template instance per primary template, this patch makes it so clang produces one instance per primary template redeclaration, tracking this new instance as a redeclation of the previous instance. Fixes #55509
1 parent 6558e56 commit 362444a

File tree

7 files changed

+129
-35
lines changed

7 files changed

+129
-35
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ Bug Fixes to C++ Support
446446
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
447447
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
448448
- Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
449+
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
449450

450451
Bug Fixes to AST Handling
451452
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaDecl.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3150,7 +3150,13 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
31503150

31513151
// Re-declaration cannot add abi_tag's.
31523152
if (const auto *NewAbiTagAttr = New->getAttr<AbiTagAttr>()) {
3153-
if (const auto *OldAbiTagAttr = Old->getAttr<AbiTagAttr>()) {
3153+
const AbiTagAttr *OldAbiTagAttr = nullptr;
3154+
for (auto *D = Old; D; D = D->getPreviousDecl()) {
3155+
OldAbiTagAttr = D->getAttr<AbiTagAttr>();
3156+
if (OldAbiTagAttr)
3157+
break;
3158+
}
3159+
if (OldAbiTagAttr) {
31543160
for (const auto &NewTag : NewAbiTagAttr->tags()) {
31553161
if (!llvm::is_contained(OldAbiTagAttr->tags(), NewTag)) {
31563162
Diag(NewAbiTagAttr->getLocation(),
@@ -12021,11 +12027,12 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1202112027

1202212028
} else {
1202312029
if (shouldLinkDependentDeclWithPrevious(NewFD, OldDecl)) {
12024-
auto *OldFD = cast<FunctionDecl>(OldDecl);
12025-
// This needs to happen first so that 'inline' propagates.
12026-
NewFD->setPreviousDeclaration(OldFD);
12027-
if (NewFD->isCXXClassMember())
12028-
NewFD->setAccess(OldFD->getAccess());
12030+
if (auto *OldFD = cast<FunctionDecl>(OldDecl); OldFD != NewFD) {
12031+
// This needs to happen first so that 'inline' propagates.
12032+
NewFD->setPreviousDeclaration(OldFD);
12033+
if (NewFD->isCXXClassMember())
12034+
NewFD->setAccess(OldFD->getAccess());
12035+
}
1202912036
}
1203012037
}
1203112038
} else if (!getLangOpts().CPlusPlus && MayNeedOverloadableChecks &&

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,9 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old,
491491
continue;
492492
}
493493

494+
if (PrevForDefaultArgs->getPrimaryTemplate() != New->getPrimaryTemplate())
495+
continue;
496+
494497
// We found the right previous declaration.
495498
break;
496499
}

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9272,16 +9272,19 @@ bool Sema::CheckFunctionTemplateSpecialization(
92729272
MarkUnusedFileScopedDecl(Specialization);
92739273
}
92749274

9275-
// Turn the given function declaration into a function template
9276-
// specialization, with the template arguments from the previous
9277-
// specialization.
9278-
// Take copies of (semantic and syntactic) template argument lists.
9279-
TemplateArgumentList *TemplArgs = TemplateArgumentList::CreateCopy(
9280-
Context, Specialization->getTemplateSpecializationArgs()->asArray());
9281-
FD->setFunctionTemplateSpecialization(
9282-
Specialization->getPrimaryTemplate(), TemplArgs, /*InsertPos=*/nullptr,
9283-
SpecInfo->getTemplateSpecializationKind(),
9284-
ExplicitTemplateArgs ? &ConvertedTemplateArgs[Specialization] : nullptr);
9275+
if (FD != Specialization) {
9276+
// Turn the given function declaration into a function template
9277+
// specialization, with the template arguments from the previous
9278+
// specialization.
9279+
// Take copies of (semantic and syntactic) template argument lists.
9280+
TemplateArgumentList *TemplArgs = TemplateArgumentList::CreateCopy(
9281+
Context, Specialization->getTemplateSpecializationArgs()->asArray());
9282+
FD->setFunctionTemplateSpecialization(
9283+
Specialization->getPrimaryTemplate(), TemplArgs, /*InsertPos=*/nullptr,
9284+
SpecInfo->getTemplateSpecializationKind(),
9285+
ExplicitTemplateArgs ? &ConvertedTemplateArgs[Specialization]
9286+
: nullptr);
9287+
}
92859288

92869289
// A function template specialization inherits the target attributes
92879290
// of its template. (We require the attributes explicitly in the

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3949,28 +3949,15 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
39493949
TemplateArgumentList::CreateCopy(Context, CanonicalBuilder);
39503950
Info.reset(SugaredDeducedArgumentList, CanonicalDeducedArgumentList);
39513951

3952+
FunctionTemplate = FunctionTemplate->getMostRecentDecl();
3953+
39523954
// Substitute the deduced template arguments into the function template
39533955
// declaration to produce the function template specialization.
39543956
DeclContext *Owner = FunctionTemplate->getDeclContext();
39553957
if (FunctionTemplate->getFriendObjectKind())
39563958
Owner = FunctionTemplate->getLexicalDeclContext();
39573959
FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
3958-
// additional check for inline friend,
3959-
// ```
3960-
// template <class F1> int foo(F1 X);
3961-
// template <int A1> struct A {
3962-
// template <class F1> friend int foo(F1 X) { return A1; }
3963-
// };
3964-
// template struct A<1>;
3965-
// int a = foo(1.0);
3966-
// ```
3967-
const FunctionDecl *FDFriend;
3968-
if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None &&
3969-
FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) &&
3970-
FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) {
3971-
FD = const_cast<FunctionDecl *>(FDFriend);
3972-
Owner = FD->getLexicalDeclContext();
3973-
}
3960+
39743961
MultiLevelTemplateArgumentList SubstArgs(
39753962
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
39763963
/*Final=*/false);

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,6 +2136,7 @@ static QualType adjustFunctionTypeForInstantiation(ASTContext &Context,
21362136
Decl *TemplateDeclInstantiator::VisitFunctionDecl(
21372137
FunctionDecl *D, TemplateParameterList *TemplateParams,
21382138
RewriteKind FunctionRewriteKind) {
2139+
FunctionDecl *PrevFunc = nullptr;
21392140
// Check whether there is already a function template specialization for
21402141
// this declaration.
21412142
FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate();
@@ -2146,9 +2147,15 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
21462147
FunctionDecl *SpecFunc
21472148
= FunctionTemplate->findSpecialization(Innermost, InsertPos);
21482149

2149-
// If we already have a function template specialization, return it.
2150-
if (SpecFunc)
2151-
return SpecFunc;
2150+
if (SpecFunc) {
2151+
if (!SpecFunc->isTemplateInstantiation())
2152+
return SpecFunc;
2153+
2154+
for (auto *Redecl : SpecFunc->redecls())
2155+
if (Redecl->getPrimaryTemplate() == FunctionTemplate)
2156+
return Redecl;
2157+
}
2158+
PrevFunc = SpecFunc;
21522159
}
21532160

21542161
bool isFriend;
@@ -2342,6 +2349,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
23422349
: Sema::LookupOrdinaryName,
23432350
D->isLocalExternDecl() ? RedeclarationKind::ForExternalRedeclaration
23442351
: SemaRef.forRedeclarationInCurContext());
2352+
if (PrevFunc)
2353+
Previous.addDecl(PrevFunc);
23452354

23462355
if (DependentFunctionTemplateSpecializationInfo *DFTSI =
23472356
D->getDependentSpecializationInfo()) {

clang/test/SemaTemplate/GH55509.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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 regression1 {
71+
template <class> class A;
72+
73+
template <class T> [[gnu::abi_tag("TAG")]] void foo(A<T>);
74+
75+
template <class> struct A {
76+
friend void foo <>(A);
77+
};
78+
79+
template struct A<int>;
80+
81+
template <class T> [[gnu::abi_tag("TAG")]] void foo(A<T>) {}
82+
83+
template void foo<int>(A<int>);
84+
} // namespace regression1

0 commit comments

Comments
 (0)