v.g.vassilev created this revision.
v.g.vassilev added a reviewer: rsmith.
v.g.vassilev added a subscriber: cfe-commits.
v.g.vassilev set the repository for this revision to rL LLVM.

Fixes a crash in modules where the template class decl becomes the most recent 
decl in the redeclaration chain and forcing the template instantiator try to 
instantiate the friend declaration, rather than the template definition.

In practice, A::list<int> produces a TemplateSpecializationType 
A::__1::list<int, allocator<type-parameter-0-0> >' failing to replace to 
subsitute the default argument to allocator<int>.


Repository:
  rL LLVM

https://reviews.llvm.org/D28399

Files:
  include/clang/AST/DeclTemplate.h
  lib/AST/ASTImporter.cpp
  lib/AST/DeclTemplate.cpp
  lib/Sema/SemaTemplate.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/Misc/ast-dump-decl.cpp
  test/Modules/Inputs/PR31469/empty.h
  test/Modules/Inputs/PR31469/module.modulemap
  test/Modules/Inputs/PR31469/textual.h
  test/Modules/Inputs/PR31469/textual_file_shadow.h
  test/Modules/pr31469.cpp

Index: test/Modules/pr31469.cpp
===================================================================
--- /dev/null
+++ test/Modules/pr31469.cpp
@@ -0,0 +1,15 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -std=c++11 -I%S/Inputs/PR31469 -verify %s
+// RUN: %clang_cc1 -std=c++11 -I%S/Inputs/PR31469 -fmodules -fmodules-local-submodule-visibility \
+// RUN:    -fimplicit-module-maps -fmodules-cache-path=%t  -verify %s
+
+#include "textual.h"
+#include "empty.h"
+
+namespace A {
+  template <class _Tp> void f();
+}
+
+A::list<int> use;
+
+// expected-no-diagnostics
Index: test/Modules/Inputs/PR31469/textual_file_shadow.h
===================================================================
--- /dev/null
+++ test/Modules/Inputs/PR31469/textual_file_shadow.h
@@ -0,0 +1,2 @@
+#include "textual.h"
+
Index: test/Modules/Inputs/PR31469/textual.h
===================================================================
--- /dev/null
+++ test/Modules/Inputs/PR31469/textual.h
@@ -0,0 +1,17 @@
+namespace A {
+inline
+namespace __1 {
+  template <class _Tp> class allocator;
+  template <class _Tp, class _Alloc = allocator<_Tp>> class list;
+  template <class _VoidPtr> class __list_iterator {
+    //template <class> friend class list; // causes another crash in ASTDeclReader::attachPreviousDecl
+    template <class, class> friend class list;
+  };
+  template <class _Tp, class _Alloc> class __list_imp {};
+  template <class _Tp, class _Alloc> class list : __list_imp<_Tp, _Alloc> {
+  public:
+    list() {}
+  };
+  template <class _Tp> void f(list<_Tp>);
+}
+}
Index: test/Modules/Inputs/PR31469/module.modulemap
===================================================================
--- /dev/null
+++ test/Modules/Inputs/PR31469/module.modulemap
@@ -0,0 +1,5 @@
+module M {
+  module "textual_shadow" { header "textual_file_shadow.h" export *}
+  module "trigger" { header "empty.h" export * }
+  export *
+}
Index: test/Modules/Inputs/PR31469/empty.h
===================================================================
--- /dev/null
+++ test/Modules/Inputs/PR31469/empty.h
@@ -0,0 +1 @@
+// This file is triggers loading of module M.
Index: test/Misc/ast-dump-decl.cpp
===================================================================
--- test/Misc/ast-dump-decl.cpp
+++ test/Misc/ast-dump-decl.cpp
@@ -336,7 +336,6 @@
   // CHECK-NEXT:       ClassTemplateDecl{{.*}} TestClassTemplate
   // CHECK-NEXT:         TemplateTypeParmDecl
   // CHECK-NEXT:         CXXRecordDecl{{.*}} class TestClassTemplate
-  // CHECK-NEXT:         ClassTemplateSpecialization{{.*}} 'TestClassTemplate'
   // CHECK-NEXT:   ClassTemplateSpecializationDecl{{.*}} class TestClassTemplate
   // CHECK-NEXT:     TemplateArgument{{.*}}A
   // CHECK-NEXT:     CXXRecordDecl{{.*}} class TestClassTemplate
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1193,8 +1193,10 @@
 
   ClassTemplateDecl *Inst
     = ClassTemplateDecl::Create(SemaRef.Context, DC, D->getLocation(),
-                                D->getIdentifier(), InstParams, RecordInst,
-                                PrevClassTemplate);
+                                D->getIdentifier(), InstParams, RecordInst);
+  assert(!(isFriend && Owner->isDependentContext()));
+  Inst->setPreviousDecl(PrevClassTemplate);
+
   RecordInst->setDescribedClassTemplate(Inst);
 
   if (isFriend) {
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -1230,7 +1230,16 @@
   ClassTemplateDecl *NewTemplate
     = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
                                 DeclarationName(Name), TemplateParams,
-                                NewClass, PrevClassTemplate);
+                                NewClass);
+
+  // If this is a templated friend in a dependent context we should not put it
+  // on the redecl chain. In some cases, the templated friend can be the most
+  // recent declaration tricking the template instantiator to make substitutions
+  // there.
+  // FIXME: Figure out how to combine with shouldLinkDependentDeclWithPrevious
+  if (!(TUK == TUK_Friend && CurContext->isDependentContext()))
+    NewTemplate->setPreviousDecl(PrevClassTemplate);
+
   NewClass->setDescribedClassTemplate(NewTemplate);
   
   if (ModulePrivateLoc.isValid())
Index: lib/AST/DeclTemplate.cpp
===================================================================
--- lib/AST/DeclTemplate.cpp
+++ lib/AST/DeclTemplate.cpp
@@ -297,12 +297,10 @@
                                              SourceLocation L,
                                              DeclarationName Name,
                                              TemplateParameterList *Params,
-                                             NamedDecl *Decl,
-                                             ClassTemplateDecl *PrevDecl) {
+                                             NamedDecl *Decl) {
   AdoptTemplateParameterList(Params, cast<DeclContext>(Decl));
   ClassTemplateDecl *New = new (C, DC) ClassTemplateDecl(C, DC, L, Name,
                                                          Params, Decl);
-  New->setPreviousDecl(PrevDecl);
   return New;
 }
 
Index: lib/AST/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -4671,8 +4671,7 @@
 
   ClassTemplateDecl *D2 = ClassTemplateDecl::Create(Importer.getToContext(), DC, 
                                                     Loc, Name, TemplateParams, 
-                                                    D2Templated, 
-                                                    /*PrevDecl=*/nullptr);
+                                                    D2Templated);
   D2Templated->setDescribedClassTemplate(D2);    
   
   D2->setAccess(D->getAccess());
Index: include/clang/AST/DeclTemplate.h
===================================================================
--- include/clang/AST/DeclTemplate.h
+++ include/clang/AST/DeclTemplate.h
@@ -2028,8 +2028,7 @@
                                    SourceLocation L,
                                    DeclarationName Name,
                                    TemplateParameterList *Params,
-                                   NamedDecl *Decl,
-                                   ClassTemplateDecl *PrevDecl);
+                                   NamedDecl *Decl);
 
   /// \brief Create an empty class template node.
   static ClassTemplateDecl *CreateDeserialized(ASTContext &C, unsigned ID);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to