From c72f348e95ced873b69abde93040060a12a1a3a6 Mon Sep 17 00:00:00 2001 From: Erik Pilkington Date: Wed, 10 Oct 2018 17:17:51 +0000 Subject: [PATCH] [Sema] Fix a multiple definition bug with friends and templates The problem was that MergeFunctionDecl sometimes needs the injected template arguments of a FunctionTemplateDecl, but is called before adding the new template to the redecl chain. This leads to multiple common pointers in the same redecl chain, each with their own identical instantiation. Fix this by merging the the common state before inserting the new template into the redecl chain. rdar://44810129 Differential revision: https://reviews.llvm.org/D53046 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@344157 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclTemplate.h | 3 +++ lib/AST/DeclTemplate.cpp | 36 +++++++++++++++++++++++++ lib/Sema/SemaDecl.cpp | 10 +++++-- test/SemaCXX/friend-template-redecl.cpp | 20 ++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 test/SemaCXX/friend-template-redecl.cpp diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index e0ea7cb8b1..ee9be2ebd0 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -1093,6 +1093,9 @@ public: /// template. ArrayRef getInjectedTemplateArgs(); + /// Merge our RedeclarableTemplateDecl::Common with \param Prev. + void mergePrevDecl(FunctionTemplateDecl *Prev); + /// Create a function template node. static FunctionTemplateDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 8e4944607c..de1b7101fb 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -300,6 +300,42 @@ ArrayRef FunctionTemplateDecl::getInjectedTemplateArgs() { return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size()); } +void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { + using Base = RedeclarableTemplateDecl; + + // If we haven't created a common pointer yet, then it can just be created + // with the usual method. + if (!Base::Common) + return; + + Common *ThisCommon = static_cast(Base::Common); + Common *PrevCommon = nullptr; + SmallVector PreviousDecls; + for (; Prev; Prev = Prev->getPreviousDecl()) { + if (Prev->Base::Common) { + PrevCommon = static_cast(Prev->Base::Common); + break; + } + PreviousDecls.push_back(Prev); + } + + // If the previous redecl chain hasn't created a common pointer yet, then just + // use this common pointer. + if (!PrevCommon) { + for (auto *D : PreviousDecls) + D->Base::Common = ThisCommon; + return; + } + + // Ensure we don't leak any important state. + assert(ThisCommon->Specializations.size() == 0 && + !ThisCommon->InstantiatedFromMember.getPointer() && + !ThisCommon->InstantiatedFromMember.getInt() && + "Can't merge incompatible declarations!"); + + Base::Common = PrevCommon; +} + //===----------------------------------------------------------------------===// // ClassTemplateDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 441c54dc35..7ef1ad2bbb 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -10022,11 +10022,17 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, if (FunctionTemplateDecl *OldTemplateDecl = dyn_cast(OldDecl)) { auto *OldFD = OldTemplateDecl->getTemplatedDecl(); - NewFD->setPreviousDeclaration(OldFD); - adjustDeclContextForDeclaratorDecl(NewFD, OldFD); FunctionTemplateDecl *NewTemplateDecl = NewFD->getDescribedFunctionTemplate(); assert(NewTemplateDecl && "Template/non-template mismatch"); + + // The call to MergeFunctionDecl above may have created some state in + // NewTemplateDecl that needs to be merged with OldTemplateDecl before we + // can add it as a redeclaration. + NewTemplateDecl->mergePrevDecl(OldTemplateDecl); + + NewFD->setPreviousDeclaration(OldFD); + adjustDeclContextForDeclaratorDecl(NewFD, OldFD); if (NewFD->isCXXClassMember()) { NewFD->setAccess(OldTemplateDecl->getAccess()); NewTemplateDecl->setAccess(OldTemplateDecl->getAccess()); diff --git a/test/SemaCXX/friend-template-redecl.cpp b/test/SemaCXX/friend-template-redecl.cpp new file mode 100644 index 0000000000..3e05964fb2 --- /dev/null +++ b/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 void bar(const T &t) { foo(t); } + +template +struct HasFriend { + template + friend void foo(const HasFriend &m) noexcept(false); +}; + +template +void foo(const HasFriend &m) noexcept(false) {} + +void f() { + HasFriend x; + foo(x); + bar(x); +} -- 2.40.0