forked from OSchip/llvm-project
[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 llvm-svn: 344157
This commit is contained in:
parent
8a39827eb3
commit
d1a184fc6a
|
@ -1093,6 +1093,9 @@ public:
|
|||
/// template.
|
||||
ArrayRef<TemplateArgument> 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,
|
||||
|
|
|
@ -300,6 +300,42 @@ ArrayRef<TemplateArgument> 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<Common *>(Base::Common);
|
||||
Common *PrevCommon = nullptr;
|
||||
SmallVector<FunctionTemplateDecl *, 8> PreviousDecls;
|
||||
for (; Prev; Prev = Prev->getPreviousDecl()) {
|
||||
if (Prev->Base::Common) {
|
||||
PrevCommon = static_cast<Common *>(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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -10022,11 +10022,17 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
|
|||
if (FunctionTemplateDecl *OldTemplateDecl =
|
||||
dyn_cast<FunctionTemplateDecl>(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());
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue