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 cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits