PR31469: Don't add friend template class decls to redecl chain in dependent contexts.

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>.
    
Kudos Richard Smith (D28399).


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@291753 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Vassil Vassilev 2017-01-12 09:16:26 +00:00
parent 5ceef6b013
commit de0c61bab9
11 changed files with 61 additions and 12 deletions

View File

@ -2028,8 +2028,7 @@ public:
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);

View File

@ -4671,8 +4671,7 @@ Decl *ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
ClassTemplateDecl *D2 = ClassTemplateDecl::Create(Importer.getToContext(), DC,
Loc, Name, TemplateParams,
D2Templated,
/*PrevDecl=*/nullptr);
D2Templated);
D2Templated->setDescribedClassTemplate(D2);
D2->setAccess(D->getAccess());

View File

@ -297,12 +297,10 @@ ClassTemplateDecl *ClassTemplateDecl::Create(ASTContext &C,
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;
}

View File

@ -1224,9 +1224,17 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
}
}
// 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
bool ShouldAddRedecl
= !(TUK == TUK_Friend && CurContext->isDependentContext());
CXXRecordDecl *NewClass =
CXXRecordDecl::Create(Context, Kind, SemanticContext, KWLoc, NameLoc, Name,
PrevClassTemplate?
PrevClassTemplate && ShouldAddRedecl ?
PrevClassTemplate->getTemplatedDecl() : nullptr,
/*DelayTypeCreation=*/true);
SetNestedNameSpecifier(NewClass, SS);
@ -1245,7 +1253,11 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
ClassTemplateDecl *NewTemplate
= ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
DeclarationName(Name), TemplateParams,
NewClass, PrevClassTemplate);
NewClass);
if (ShouldAddRedecl)
NewTemplate->setPreviousDecl(PrevClassTemplate);
NewClass->setDescribedClassTemplate(NewTemplate);
if (ModulePrivateLoc.isValid())

View File

@ -1219,8 +1219,10 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
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) {

View File

@ -336,7 +336,6 @@ namespace testCanonicalTemplate {
// 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

View File

@ -0,0 +1 @@
// This file is triggers loading of module M.

View File

@ -0,0 +1,5 @@
module M {
module "textual_shadow" { header "textual_file_shadow.h" export *}
module "trigger" { header "empty.h" export * }
export *
}

View File

@ -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>);
}
}

View File

@ -0,0 +1,2 @@
#include "textual.h"

15
test/Modules/pr31469.cpp Normal file
View File

@ -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