]> granicus.if.org Git - clang/commitdiff
Fix a couple of class template argument deduction crashes with libc++'s tuple.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 7 Jun 2017 02:42:27 +0000 (02:42 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 7 Jun 2017 02:42:27 +0000 (02:42 +0000)
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

include/clang/AST/RecursiveASTVisitor.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/SemaCXX/cxx1z-class-template-argument-deduction.cpp

index cd2a39449825b4d2db838be919d6a2bec5461d3b..ad3f40d0d3f64cfd2ad55e173944e5f32a252f69 100644 (file)
@@ -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, {
index a5a5c74afe69609608ef9131b1983891a52882c9..aa73a6934518781ed31c5a62b1995e784cf539c9 100644 (file)
@@ -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<RedeclaredClassMember>;
index 741a3a471667de18b695f6a03fbd04a7e3d9e47b..c872d09d22187e325627b8f6182f638ee36cadf1 100644 (file)
@@ -7640,6 +7640,9 @@ public:
                           LateInstantiatedAttrVec *LateAttrs = nullptr,
                           LocalInstantiationScope *OuterMostScope = nullptr);
 
+  bool usesPartialOrExplicitSpecialization(
+      SourceLocation Loc, ClassTemplateSpecializationDecl *ClassTemplateSpec);
+
   bool
   InstantiateClassTemplateSpecialization(SourceLocation PointOfInstantiation,
                            ClassTemplateSpecializationDecl *ClassTemplateSpec,
index a654ca800b05f52d03c7ffc8a3c4acf35ddde986..2279c9ca55026f659e265417ea21e47a8da6612a 100644 (file)
@@ -2350,6 +2350,25 @@ namespace {
   };
 }
 
+bool Sema::usesPartialOrExplicitSpecialization(
+    SourceLocation Loc, ClassTemplateSpecializationDecl *ClassTemplateSpec) {
+  if (ClassTemplateSpec->getTemplateSpecializationKind() ==
+      TSK_ExplicitSpecialization)
+    return true;
+
+  SmallVector<ClassTemplatePartialSpecializationDecl *, 4> 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).
index e7523ce2836dd7b5f4e396728bd065903188d9d3..9e8b4d55d63e8f129e2cbf0ade23eb307c4a3322 100644 (file)
@@ -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<ClassTemplateSpecializationDecl>(SubstRecord))) {
+            Diag(Loc, diag::err_specialization_not_primary_template)
+              << T << (SubstRecord->getTemplateSpecializationKind() ==
+                           TSK_ExplicitSpecialization);
+            return nullptr;
+          }
+          DC = SubstRecord;
           continue;
         }
       }
index 5de228ad2857455b3ddec2a9ff9a8e4797e541d3..ede2844c235a594a3643bbf500d0778eeb54e446 100644 (file)
@@ -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<class ...T> struct tuple {
+      template<class ...Args> struct X { static const bool value = false; };
+      template<class ...U, bool Y = X<U...>::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<class ...T> struct tuple { // expected-note {{candidate}}
+      template<class ...Args> struct X { static const bool value = false; };
+      template<class ...U, bool Y = X<U...>::value> tuple(U &&...u);
+      // expected-note@-1 {{substitution failure [with T = <>, U = <int, int, int>]: 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}}
+  }
+}