]> granicus.if.org Git - clang/commitdiff
[Sema] Fix a multiple definition bug with friends and templates
authorErik Pilkington <erik.pilkington@gmail.com>
Wed, 10 Oct 2018 17:17:51 +0000 (17:17 +0000)
committerErik Pilkington <erik.pilkington@gmail.com>
Wed, 10 Oct 2018 17:17:51 +0000 (17:17 +0000)
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
lib/AST/DeclTemplate.cpp
lib/Sema/SemaDecl.cpp
test/SemaCXX/friend-template-redecl.cpp [new file with mode: 0644]

index e0ea7cb8b1b83cf26625e37ea811986cac5abbe6..ee9be2ebd0eb25a253f94f4f75a06fbec52a3115 100644 (file)
@@ -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,
index 8e4944607c0ac3f540972d71cf1d5a5418a090b1..de1b7101fb93607f7a1677a502757894734cc313 100644 (file)
@@ -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
 //===----------------------------------------------------------------------===//
index 441c54dc3582fbf77e17e1da9f00aaf01aa72639..7ef1ad2bbb0e96f75ca076eba61062b64fe9a985 100644 (file)
@@ -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());
diff --git a/test/SemaCXX/friend-template-redecl.cpp b/test/SemaCXX/friend-template-redecl.cpp
new file mode 100644 (file)
index 0000000..3e05964
--- /dev/null
@@ -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);
+}