From: Richard Smith Date: Wed, 7 Jun 2017 02:42:27 +0000 (+0000) Subject: Fix a couple of class template argument deduction crashes with libc++'s tuple. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2695ac7becb347f62273040501dc6b858ddd68e9;p=clang Fix a couple of class template argument deduction crashes with libc++'s tuple. RecursiveASTVisitor was not properly recursing through a SubstTemplateTypeParmTypes, resulting in crashes in pack expansion where we couldn't always find an unexpanded pack within a pack expansion. We also have an issue where substitution of deduced template arguments for an implicit deduction guide creates the "impossible" case of naming a non-dependent member of the current instantiation, but within a specialization that is actually instantiated from a different (partial/explicit) specialization of the template. We resolve this by declaring that constructors that do so can only be used to deduce specializations of the primary template. I'm running this past CWG to see if people agree this is the right thing to do. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@304862 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index cd2a394498..ad3f40d0d3 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1021,8 +1021,12 @@ DEF_TRAVERSE_TYPE(DeducedTemplateSpecializationType, { DEF_TRAVERSE_TYPE(RecordType, {}) DEF_TRAVERSE_TYPE(EnumType, {}) DEF_TRAVERSE_TYPE(TemplateTypeParmType, {}) -DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, {}) -DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, {}) +DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, { + TRY_TO(TraverseType(T->getReplacementType())); +}) +DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, { + TRY_TO(TraverseTemplateArgument(T->getArgumentPack())); +}) DEF_TRAVERSE_TYPE(TemplateSpecializationType, { TRY_TO(TraverseTemplateName(T->getTemplateName())); @@ -1249,8 +1253,12 @@ DEF_TRAVERSE_TYPELOC(DeducedTemplateSpecializationType, { DEF_TRAVERSE_TYPELOC(RecordType, {}) DEF_TRAVERSE_TYPELOC(EnumType, {}) DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, {}) -DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, {}) -DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, {}) +DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, { + TRY_TO(TraverseType(TL.getTypePtr()->getReplacementType())); +}) +DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, { + TRY_TO(TraverseTemplateArgument(TL.getTypePtr()->getArgumentPack())); +}) // FIXME: use the loc for the template name? DEF_TRAVERSE_TYPELOC(TemplateSpecializationType, { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a5a5c74afe..aa73a69345 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5613,6 +5613,11 @@ def err_enumerator_does_not_exist : Error< def note_enum_specialized_here : Note< "enum %0 was explicitly specialized here">; +def err_specialization_not_primary_template : Error< + "cannot reference member of primary template because deduced class " + "template specialization %0 is %select{instantiated from a partial|" + "an explicit}1 specialization">; + def err_member_redeclared : Error<"class member cannot be redeclared">; def ext_member_redeclared : ExtWarn<"class member cannot be redeclared">, InGroup; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 741a3a4716..c872d09d22 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7640,6 +7640,9 @@ public: LateInstantiatedAttrVec *LateAttrs = nullptr, LocalInstantiationScope *OuterMostScope = nullptr); + bool usesPartialOrExplicitSpecialization( + SourceLocation Loc, ClassTemplateSpecializationDecl *ClassTemplateSpec); + bool InstantiateClassTemplateSpecialization(SourceLocation PointOfInstantiation, ClassTemplateSpecializationDecl *ClassTemplateSpec, diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index a654ca800b..2279c9ca55 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -2350,6 +2350,25 @@ namespace { }; } +bool Sema::usesPartialOrExplicitSpecialization( + SourceLocation Loc, ClassTemplateSpecializationDecl *ClassTemplateSpec) { + if (ClassTemplateSpec->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization) + return true; + + SmallVector PartialSpecs; + ClassTemplateSpec->getSpecializedTemplate() + ->getPartialSpecializations(PartialSpecs); + for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) { + TemplateDeductionInfo Info(Loc); + if (!DeduceTemplateArguments(PartialSpecs[I], + ClassTemplateSpec->getTemplateArgs(), Info)) + return true; + } + + return false; +} + /// Get the instantiation pattern to use to instantiate the definition of a /// given ClassTemplateSpecializationDecl (either the pattern of the primary /// template or of a partial specialization). diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index e7523ce283..9e8b4d55d6 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5000,7 +5000,21 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, QualType T = CheckTemplateIdType(TemplateName(TD), Loc, Args); if (T.isNull()) return nullptr; - DC = T->getAsCXXRecordDecl(); + auto *SubstRecord = T->getAsCXXRecordDecl(); + assert(SubstRecord && "class template id not a class type?"); + // Check that this template-id names the primary template and not a + // partial or explicit specialization. (In the latter cases, it's + // meaningless to attempt to find an instantiation of D within the + // specialization.) + // FIXME: The standard doesn't say what should happen here. + if (usesPartialOrExplicitSpecialization(Loc, + cast(SubstRecord))) { + Diag(Loc, diag::err_specialization_not_primary_template) + << T << (SubstRecord->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization); + return nullptr; + } + DC = SubstRecord; continue; } } diff --git a/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp index 5de228ad28..ede2844c23 100644 --- a/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp +++ b/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp @@ -248,3 +248,27 @@ namespace variadic { }; Z z(1, a, b); } + +namespace tuple_tests { + // The converting n-ary constructor appears viable, deducing T as an empty + // pack (until we check its SFINAE constraints). + namespace libcxx_1 { + template struct tuple { + template struct X { static const bool value = false; }; + template::value> tuple(U &&...u); + }; + tuple a = {1, 2, 3}; + } + + // Don't get caught by surprise when X<...> doesn't even exist in the + // selected specialization! + namespace libcxx_2 { + template struct tuple { // expected-note {{candidate}} + template struct X { static const bool value = false; }; + template::value> tuple(U &&...u); + // expected-note@-1 {{substitution failure [with T = <>, U = ]: cannot reference member of primary template because deduced class template specialization 'tuple<>' is an explicit specialization}} + }; + template <> class tuple<> {}; + tuple a = {1, 2, 3}; // expected-error {{no viable constructor or deduction guide}} + } +}