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

0 commit comments

Comments
 (0)