erik.pilkington created this revision.
erik.pilkington added reviewers: rsmith, rjmccall.
Herald added a subscriber: dexonsmith.
Clang used to error out on the attached testcase, due to multiple definitions
of `foo<int>`. The problem is that multiple FunctionTemplateDecl::Common
pointers are created for the two FunctionTemplateDeclarations `foo` in the
redeclaration chain, each with their own copy of the instantiation of `f<int>`.
This patch fixes the problem by using the previous function template in a call
to `FuntionTemplateDecl::getInjectedTemplateArgs()` (this was the call that
caused the Common pointer to be allocated in the redeclaration). Most of this
patch is just boilerplate to get the function template declaration down the
stack to where it's needed.
Fixes rdar://44810129
Thanks for taking a look!
Erik
Repository:
rC Clang
https://reviews.llvm.org/D53046
Files:
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaExceptionSpec.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/test/SemaCXX/friend-template-redecl.cpp
Index: clang/test/SemaCXX/friend-template-redecl.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/friend-template-redecl.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++17 -verify -emit-llvm-only %s
+
+// expected-no-diagnostics
+
+template <class T> void bar(const T &t) { foo(t); }
+
+template <class>
+struct HasFriend {
+ template <class T>
+ friend void foo(const HasFriend<T> &m) noexcept(false);
+};
+
+template <class T>
+void foo(const HasFriend<T> &m) noexcept(false) {}
+
+void f() {
+ HasFriend<int> x;
+ foo(x);
+ bar(x);
+}
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3575,7 +3575,8 @@
}
void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
- FunctionDecl *Decl) {
+ FunctionDecl *Decl,
+ FunctionDecl *OldDecl) {
const FunctionProtoType *Proto = Decl->getType()->castAs<FunctionProtoType>();
if (Proto->getExceptionSpecType() != EST_Uninstantiated)
return;
@@ -3601,8 +3602,9 @@
Sema::ContextRAII savedContext(*this, Decl);
LocalInstantiationScope Scope(*this);
- MultiLevelTemplateArgumentList TemplateArgs =
- getTemplateInstantiationArgs(Decl, nullptr, /*RelativeToPrimary*/true);
+ MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
+ Decl, nullptr, /*RelativeToPrimary*/ true, nullptr,
+ !OldDecl ? nullptr : OldDecl->getDescribedFunctionTemplate());
FunctionDecl *Template = Proto->getExceptionSpecTemplate();
if (addInstantiatedParametersToScope(*this, Decl, Template, Scope,
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -51,11 +51,16 @@
/// instantiating the definition of the given declaration, \p D. This is
/// used to determine the proper set of template instantiation arguments for
/// friend function template specializations.
+///
+/// \param PrevFunTmpl If non-NULL, D is a pattern declaration of a function
+/// template that hasn't yet been attached to the redeclaration chain of
+/// PrevFunTmpl's pattern.
MultiLevelTemplateArgumentList
Sema::getTemplateInstantiationArgs(NamedDecl *D,
const TemplateArgumentList *Innermost,
bool RelativeToPrimary,
- const FunctionDecl *Pattern) {
+ const FunctionDecl *Pattern,
+ FunctionTemplateDecl *PrevFunTmpl) {
// Accumulate the set of template argument lists in this structure.
MultiLevelTemplateArgumentList Result;
@@ -151,6 +156,16 @@
} else if (FunctionTemplateDecl *FunTmpl
= Function->getDescribedFunctionTemplate()) {
+ if (PrevFunTmpl) {
+ // FunTmpl hasn't been linked into the same redecl chain as
+ // PrevFunTmpl yet, so we can't retreive it's injected template
+ // arguments just yet; use PrevFunTmpl's instead.
+ //
+ // Note that we don't need to verify that FunTmpl is the function
+ // template that is a redeclaration of PrevFunTmpl because only one
+ // function template decl can appear in this search.
+ FunTmpl = PrevFunTmpl;
+ }
// Add the "injected" template arguments.
Result.addOuterTemplateArguments(FunTmpl->getInjectedTemplateArgs());
}
Index: clang/lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- clang/lib/Sema/SemaExceptionSpec.cpp
+++ clang/lib/Sema/SemaExceptionSpec.cpp
@@ -186,7 +186,8 @@
}
const FunctionProtoType *
-Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
+Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT,
+ FunctionDecl *OldDecl) {
if (FPT->getExceptionSpecType() == EST_Unparsed) {
Diag(Loc, diag::err_exception_spec_not_parsed);
return nullptr;
@@ -207,7 +208,7 @@
if (SourceFPT->getExceptionSpecType() == EST_Unevaluated)
EvaluateImplicitExceptionSpec(Loc, cast<CXXMethodDecl>(SourceDecl));
else
- InstantiateExceptionSpec(Loc, SourceDecl);
+ InstantiateExceptionSpec(Loc, SourceDecl, OldDecl);
const FunctionProtoType *Proto =
SourceDecl->getType()->castAs<FunctionProtoType>();
@@ -246,7 +247,8 @@
const FunctionProtoType *New, SourceLocation NewLoc,
bool *MissingExceptionSpecification = nullptr,
bool *MissingEmptyExceptionSpecification = nullptr,
- bool AllowNoexceptAllMatchWithNoSpec = false, bool IsOperatorNew = false);
+ bool AllowNoexceptAllMatchWithNoSpec = false, bool IsOperatorNew = false,
+ FunctionDecl *OldDecl = nullptr);
/// Determine whether a function has an implicitly-generated exception
/// specification.
@@ -303,7 +305,7 @@
Old->getType()->getAs<FunctionProtoType>(), Old->getLocation(),
New->getType()->getAs<FunctionProtoType>(), New->getLocation(),
&MissingExceptionSpecification, &MissingEmptyExceptionSpecification,
- /*AllowNoexceptAllMatchWithNoSpec=*/true, IsOperatorNew)) {
+ /*AllowNoexceptAllMatchWithNoSpec=*/true, IsOperatorNew, Old)) {
// C++11 [except.spec]p4 [DR1492]:
// If a declaration of a function has an implicit
// exception-specification, other declarations of the function shall
@@ -487,7 +489,8 @@
const FunctionProtoType *New, SourceLocation NewLoc,
bool *MissingExceptionSpecification,
bool *MissingEmptyExceptionSpecification,
- bool AllowNoexceptAllMatchWithNoSpec, bool IsOperatorNew) {
+ bool AllowNoexceptAllMatchWithNoSpec, bool IsOperatorNew,
+ FunctionDecl *OldDecl) {
if (MissingExceptionSpecification)
*MissingExceptionSpecification = false;
@@ -497,7 +500,7 @@
Old = S.ResolveExceptionSpec(NewLoc, Old);
if (!Old)
return false;
- New = S.ResolveExceptionSpec(NewLoc, New);
+ New = S.ResolveExceptionSpec(NewLoc, New, OldDecl);
if (!New)
return false;
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -10009,6 +10009,7 @@
}
if (Redeclaration) {
+
// NewFD and OldDecl represent declarations that need to be
// merged.
if (MergeFunctionDecl(NewFD, OldDecl, S, MergeTypeWithPrevious)) {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -1452,8 +1452,9 @@
static QualType GetTypeFromParser(ParsedType Ty,
TypeSourceInfo **TInfo = nullptr);
CanThrowResult canThrow(const Expr *E);
- const FunctionProtoType *ResolveExceptionSpec(SourceLocation Loc,
- const FunctionProtoType *FPT);
+ const FunctionProtoType *
+ ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT,
+ FunctionDecl *OldDecl = nullptr);
void UpdateExceptionSpec(FunctionDecl *FD,
const FunctionProtoType::ExceptionSpecInfo &ESI);
bool CheckSpecifiedExceptionType(QualType &T, SourceRange Range);
@@ -7124,7 +7125,8 @@
getTemplateInstantiationArgs(NamedDecl *D,
const TemplateArgumentList *Innermost = nullptr,
bool RelativeToPrimary = false,
- const FunctionDecl *Pattern = nullptr);
+ const FunctionDecl *Pattern = nullptr,
+ FunctionTemplateDecl *PrevFTD = nullptr);
/// A context in which code is being synthesized (where a source location
/// alone is not sufficient to identify the context). This covers template
@@ -7861,7 +7863,8 @@
const MultiLevelTemplateArgumentList &TemplateArgs);
void InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
- FunctionDecl *Function);
+ FunctionDecl *Function,
+ FunctionDecl *OldFunction = nullptr);
FunctionDecl *InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD,
const TemplateArgumentList *Args,
SourceLocation Loc);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits