Skip to content

Commit 1282f6a

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 925d347 commit 1282f6a

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);

0 commit comments

Comments
 (0)