]> granicus.if.org Git - clang/commitdiff
Once we have deduced the template arguments of a class template
authorDouglas Gregor <dgregor@apple.com>
Thu, 11 Jun 2009 18:10:32 +0000 (18:10 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 11 Jun 2009 18:10:32 +0000 (18:10 +0000)
partial specialization, substitute those template arguments back into
the template arguments of the class template partial specialization to
see if the results still match the original template arguments.

This code is more general than it needs to be, since we don't yet
diagnose C++ [temp.class.spec]p9. However, it's likely to be needed
for function templates.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@73196 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/Sema.h
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateDeduction.cpp
lib/Sema/SemaTemplateInstantiate.cpp
test/SemaTemplate/temp_class_spec.cpp

index 866f898391dba2468693072e071414d5cea10af7..2df1ae295044bdb1564bf9e20bfcbdb8c1eecf2d 100644 (file)
@@ -1995,7 +1995,7 @@ public:
   bool CheckTemplateArgumentPointerToMember(Expr *Arg, NamedDecl *&Member);
   bool CheckTemplateArgument(NonTypeTemplateParmDecl *Param, 
                              QualType InstantiatedParamType, Expr *&Arg,
-                             TemplateArgumentListBuilder *Converted = 0);
+                             TemplateArgument &Converted);
   bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, DeclRefExpr *Arg);
   bool TemplateParameterListsAreEqual(TemplateParameterList *New,
                                       TemplateParameterList *Old,
index f03c1acb8e2d3dbfa4fc62f12824c8e99b8be077..7c1c7cf0e19663325b5d97aa27cfe721d16ecf02 100644 (file)
@@ -297,7 +297,9 @@ void Sema::ActOnNonTypeTemplateParameterDefault(DeclPtrTy TemplateParamD,
   // FIXME: Implement this check! Needs a recursive walk over the types.
   
   // Check the well-formedness of the default template argument.
-  if (CheckTemplateArgument(TemplateParm, TemplateParm->getType(), Default)) {
+  TemplateArgument Converted;
+  if (CheckTemplateArgument(TemplateParm, TemplateParm->getType(), Default,
+                            Converted)) {
     TemplateParm->setInvalidDecl();
     return;
   }
@@ -1116,8 +1118,11 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
           
       case TemplateArgument::Expression: {
         Expr *E = Arg.getAsExpr();
-        if (CheckTemplateArgument(NTTP, NTTPType, E, &Converted))
+        TemplateArgument Result;
+        if (CheckTemplateArgument(NTTP, NTTPType, E, Result))
           Invalid = true;
+        else
+          Converted.push_back(Result);
         break;
       }
 
@@ -1401,11 +1406,10 @@ Sema::CheckTemplateArgumentPointerToMember(Expr *Arg, NamedDecl *&Member) {
 /// InstantiatedParamType is the type of the non-type template
 /// parameter after it has been instantiated.
 ///
-/// If Converted is non-NULL and no errors occur, the value
-/// of this argument will be added to the end of the Converted vector.
+/// If no error was detected, Converted receives the converted template argument.
 bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
                                  QualType InstantiatedParamType, Expr *&Arg, 
-                                 TemplateArgumentListBuilder *Converted) {
+                                 TemplateArgument &Converted) {
   SourceLocation StartLoc = Arg->getSourceRange().getBegin();
 
   // If either the parameter has a dependent type or the argument is
@@ -1413,8 +1417,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
   // FIXME: Add template argument to Converted!
   if (InstantiatedParamType->isDependentType() || Arg->isTypeDependent()) {
     // FIXME: Produce a cloned, canonical expression?
-    if (Converted)
-      Converted->push_back(TemplateArgument(Arg));
+    Converted = TemplateArgument(Arg);
     return false;
   }
 
@@ -1479,7 +1482,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
 
     QualType IntegerType = Context.getCanonicalType(ParamType);
     if (const EnumType *Enum = IntegerType->getAsEnumType())
-      IntegerType = Enum->getDecl()->getIntegerType();
+      IntegerType = Context.getCanonicalType(Enum->getDecl()->getIntegerType());
 
     if (!Arg->isValueDependent()) {
       // Check that an unsigned parameter does not receive a negative
@@ -1509,21 +1512,19 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
       Value.setIsSigned(IntegerType->isSignedIntegerType());
     }
 
-    if (Converted) {
-      // Add the value of this argument to the list of converted
-      // arguments. We use the bitwidth and signedness of the template
-      // parameter.
-      if (Arg->isValueDependent()) {
-        // The argument is value-dependent. Create a new
-        // TemplateArgument with the converted expression.
-        Converted->push_back(TemplateArgument(Arg));
-        return false;
-      } 
-
-      Converted->push_back(TemplateArgument(StartLoc, Value,
-                      ParamType->isEnumeralType() ? ParamType : IntegerType));
+    // Add the value of this argument to the list of converted
+    // arguments. We use the bitwidth and signedness of the template
+    // parameter.
+    if (Arg->isValueDependent()) {
+      // The argument is value-dependent. Create a new
+      // TemplateArgument with the converted expression.
+      Converted = TemplateArgument(Arg);
+      return false;
     }
 
+    Converted = TemplateArgument(StartLoc, Value,
+                                 ParamType->isEnumeralType() ? ParamType 
+                                                             : IntegerType);
     return false;
   }
 
@@ -1590,11 +1591,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
       if (CheckTemplateArgumentPointerToMember(Arg, Member))
         return true;
 
-      if (Converted) {
-        Member = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Member));
-        Converted->push_back(TemplateArgument(StartLoc, Member));
-      }
-
+      Member = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Member));
+      Converted = TemplateArgument(StartLoc, Member);
       return false;
     }
     
@@ -1602,10 +1600,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
     if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
       return true;
 
-    if (Converted) {
-      Entity = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Entity));
-      Converted->push_back(TemplateArgument(StartLoc, Entity));
-    }
+    Entity = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Entity));
+    Converted = TemplateArgument(StartLoc, Entity);
     return false;
   }
 
@@ -1643,11 +1639,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
     if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
       return true;
 
-    if (Converted) {
-      Entity = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Entity));
-      Converted->push_back(TemplateArgument(StartLoc, Entity));
-    }
-
+    Entity = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Entity));
+    Converted = TemplateArgument(StartLoc, Entity);
     return false;
   }
     
@@ -1687,11 +1680,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
     if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
       return true;
 
-    if (Converted) {
-      Entity = cast<NamedDecl>(Context.getCanonicalDecl(Entity));
-      Converted->push_back(TemplateArgument(StartLoc, Entity));
-    }
-
+    Entity = cast<NamedDecl>(Context.getCanonicalDecl(Entity));
+    Converted = TemplateArgument(StartLoc, Entity);
     return false;
   }
 
@@ -1719,11 +1709,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
   if (CheckTemplateArgumentPointerToMember(Arg, Member))
     return true;
   
-  if (Converted) {
-    Member = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Member));
-    Converted->push_back(TemplateArgument(StartLoc, Member));
-  }
-
+  Member = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Member));
+  Converted = TemplateArgument(StartLoc, Member);
   return false;
 }
 
index 6e7a47aeb298ca5d66a1a693b8eef4cbf7224d1d..fe64946e14db1ba4a868990a0887371c22bd43a8 100644 (file)
@@ -531,30 +531,36 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
                              Deduced.data(), Deduced.size());
   if (Inst)
     return 0;
-  
-  // FIXME: Substitute the deduced template arguments into the template
-  // arguments of the class template partial specialization; the resulting
-  // template arguments should match TemplateArgs exactly.
-  
-  for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
-    TemplateArgument &Arg = Deduced[I];
 
-    // FIXME: If this template argument was not deduced, but the corresponding
-    // template parameter has a default argument, instantiate the default
-    // argument.
-    if (Arg.isNull()) // FIXME: Result->Destroy(Context);
+  // C++ [temp.deduct.type]p2:
+  //   [...] or if any template argument remains neither deduced nor
+  //   explicitly specified, template argument deduction fails.
+  TemplateArgumentListBuilder Builder(Context);
+  for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
+    if (Deduced[I].isNull())
       return 0;
-    
+
+    Builder.push_back(Deduced[I]);
+  }
+
+  // Form the template argument list from the deduced template arguments.
+  TemplateArgumentList *DeducedArgumentList 
+    = new (Context) TemplateArgumentList(Context, Builder, /*CopyArgs=*/true,
+                                         /*FlattenArgs=*/true);
+
+  // Now that we have all of the deduced template arguments, take
+  // another pass through them to convert any integral template
+  // arguments to the appropriate type.
+  for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
+    TemplateArgument &Arg = Deduced[I];    
     if (Arg.getKind() == TemplateArgument::Integral) {
-      // FIXME: Instantiate the type, but we need some context!
       const NonTypeTemplateParmDecl *Parm 
         = cast<NonTypeTemplateParmDecl>(Partial->getTemplateParameters()
                                           ->getParam(I));
-      //      QualType T = InstantiateType(Parm->getType(), *Result,
-      //                                   Parm->getLocation(), Parm->getDeclName());
-      //      if (T.isNull()) // FIXME: Result->Destroy(Context);
-      //        return 0;
-      QualType T = Parm->getType();
+      QualType T = InstantiateType(Parm->getType(), *DeducedArgumentList,
+                                   Parm->getLocation(), Parm->getDeclName());
+      if (T.isNull()) // FIXME: DeducedArgumentList->Destroy(Context);
+        return 0;
       
       // FIXME: Make sure we didn't overflow our data type!
       llvm::APSInt &Value = *Arg.getAsIntegral();
@@ -564,14 +570,134 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
       Value.setIsSigned(T->isSignedIntegerType());
       Arg.setIntegralType(T);
     }
+
+    (*DeducedArgumentList)[I] = Arg;
   }
-  
-  // FIXME: This is terrible. DeduceTemplateArguments should use a 
-  // TemplateArgumentListBuilder directly.
-  TemplateArgumentListBuilder Builder(Context);
-  for (unsigned I = 0, N = Deduced.size(); I != N; ++I)
-    Builder.push_back(Deduced[I]);
-  
-  return new (Context) TemplateArgumentList(Context, Builder, /*CopyArgs=*/true,
-                                            /*FlattenArgs=*/true);
+
+  // Substitute the deduced template arguments into the template
+  // arguments of the class template partial specialization, and
+  // verify that the instantiated template arguments are both valid
+  // and are equivalent to the template arguments originally provided
+  // to the class template.
+  ClassTemplateDecl *ClassTemplate = Partial->getSpecializedTemplate();
+  const TemplateArgumentList &PartialTemplateArgs = Partial->getTemplateArgs();
+  for (unsigned I = 0, N = PartialTemplateArgs.flat_size(); I != N; ++I) {
+    TemplateArgument InstArg = Instantiate(PartialTemplateArgs[I],
+                                           *DeducedArgumentList);
+    if (InstArg.isNull()) {
+      // FIXME: DeducedArgumentList->Destroy(Context); (or use RAII)
+      return 0;
+    }
+
+    Decl *Param 
+      = const_cast<Decl *>(ClassTemplate->getTemplateParameters()->getParam(I));
+    if (isa<TemplateTypeParmDecl>(Param)) {
+      if (InstArg.getKind() != TemplateArgument::Type ||
+          Context.getCanonicalType(InstArg.getAsType())
+            != Context.getCanonicalType(TemplateArgs[I].getAsType()))
+        // FIXME: DeducedArgumentList->Destroy(Context); (or use RAII)
+        return 0;
+    } else if (NonTypeTemplateParmDecl *NTTP 
+                 = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+      QualType T = InstantiateType(NTTP->getType(), TemplateArgs,
+                                   NTTP->getLocation(), NTTP->getDeclName());
+      if (T.isNull())
+        // FIXME: DeducedArgumentList->Destroy(Context); (or use RAII)
+        return 0;
+
+      if (InstArg.getKind() == TemplateArgument::Declaration ||
+          InstArg.getKind() == TemplateArgument::Expression) {
+        // Turn the template argument into an expression, so that we can
+        // perform type checking on it and convert it to the type of the
+        // non-type template parameter. FIXME: Will this expression be
+        // leaked? It's hard to tell, since our ownership model for
+        // expressions in template arguments is so poor.
+        Expr *E = 0;
+        if (InstArg.getKind() == TemplateArgument::Declaration) {
+          NamedDecl *D = cast<NamedDecl>(InstArg.getAsDecl());
+          QualType T = Context.OverloadTy;
+          if (ValueDecl *VD = dyn_cast<ValueDecl>(D))
+            T = VD->getType().getNonReferenceType();
+          E = new (Context) DeclRefExpr(D, T, InstArg.getLocation());
+        } else {
+          E = InstArg.getAsExpr();
+        }
+
+        // Check that the template argument can be used to initialize
+        // the corresponding template parameter.
+        if (CheckTemplateArgument(NTTP, T, E, InstArg))
+          return 0;
+      }
+
+      switch (InstArg.getKind()) {
+      case TemplateArgument::Null:
+        assert(false && "Null template arguments cannot get here");
+        return 0;
+
+      case TemplateArgument::Type:
+        assert(false && "Type/value mismatch");
+        return 0;
+
+      case TemplateArgument::Integral: {
+        llvm::APSInt &Value = *InstArg.getAsIntegral();
+        if (T->isIntegralType() || T->isEnumeralType()) {
+          QualType IntegerType = Context.getCanonicalType(T);
+          if (const EnumType *Enum = dyn_cast<EnumType>(IntegerType))
+            IntegerType = Context.getCanonicalType(
+                                           Enum->getDecl()->getIntegerType());
+
+          // Check that an unsigned parameter does not receive a negative
+          // value.
+          if (IntegerType->isUnsignedIntegerType()
+              && (Value.isSigned() && Value.isNegative()))
+            return 0;
+
+          // Check for truncation. If the number of bits in the
+          // instantiated template argument exceeds what is allowed by
+          // the type, template argument deduction fails.
+          unsigned AllowedBits = Context.getTypeSize(IntegerType);
+          if (Value.getActiveBits() > AllowedBits)
+            return 0;
+
+          if (Value.getBitWidth() != AllowedBits)
+            Value.extOrTrunc(AllowedBits);
+          Value.setIsSigned(IntegerType->isSignedIntegerType());
+
+          // Check that the instantiated value is the same as the
+          // value provided as a template argument.
+          if (Value != *TemplateArgs[I].getAsIntegral())
+            return 0;
+        } else if (T->isPointerType() || T->isMemberPointerType()) {
+          // Deal with NULL pointers that are used to initialize
+          // pointer and pointer-to-member non-type template
+          // parameters (C++0x).
+          if (TemplateArgs[I].getAsDecl()) 
+            return 0; // Not a NULL declaration
+
+          // Check that the integral value is 0, the NULL pointer
+          // constant.
+          if (Value != 0)
+            return 0;
+        } else
+          return 0;
+        break;
+      }
+
+      case TemplateArgument::Declaration:
+        if (Context.getCanonicalDecl(InstArg.getAsDecl())
+              != Context.getCanonicalDecl(TemplateArgs[I].getAsDecl()))
+          return 0;
+        break;
+
+      case TemplateArgument::Expression:
+        // FIXME: Check equality of expressions
+        break;
+      }
+    } else {
+      assert(isa<TemplateTemplateParmDecl>(Param));
+      // FIXME: Check template template arguments
+    }
+  }
+
+  return DeducedArgumentList;
 }
index ee74b9a8bdf407a59a734db7e92db0bf74415018..964d3b14853f8e45fad0e9617c41ff49e5377eab 100644 (file)
@@ -185,18 +185,12 @@ void Sema::PrintInstantiationStack() {
     case ActiveTemplateInstantiation::PartialSpecDeductionInstantiation: {
       ClassTemplatePartialSpecializationDecl *PartialSpec
         = cast<ClassTemplatePartialSpecializationDecl>((Decl *)Active->Entity);
-      std::string TemplateArgsStr
-        = TemplateSpecializationType::PrintTemplateArgumentList(
-                        PartialSpec->getTemplateArgs().getFlatArgumentList(),
-                                  PartialSpec->getTemplateArgs().flat_size(),
-                                                      Context.PrintingPolicy);
       // FIXME: The active template instantiation's template arguments
       // are interesting, too. We should add something like [with T =
       // foo, U = bar, etc.] to the string.
       Diags.Report(FullSourceLoc(Active->PointOfInstantiation, SourceMgr),
                    diag::note_partial_spec_deduct_instantiation_here)
-        << (PartialSpec->getSpecializedTemplate()->getNameAsString() + 
-            TemplateArgsStr)
+        << Context.getTypeDeclType(PartialSpec)
         << Active->InstantiationRange;
       break;
     }
@@ -441,7 +435,7 @@ InstantiateFunctionProtoType(const FunctionProtoType *T,
     ParamTypes.push_back(P);
   }
 
-  return SemaRef.BuildFunctionType(ResultType, &ParamTypes[0]
+  return SemaRef.BuildFunctionType(ResultType, ParamTypes.data()
                                    ParamTypes.size(),
                                    T->isVariadic(), T->getTypeQuals(),
                                    Loc, Entity);
@@ -567,7 +561,7 @@ InstantiateTemplateSpecializationType(
                                                       TemplateArgs);
 
   return SemaRef.CheckTemplateIdType(Name, Loc, SourceLocation(),
-                                     &InstantiatedTemplateArgs[0],
+                                     InstantiatedTemplateArgs.data(),
                                      InstantiatedTemplateArgs.size(),
                                      SourceLocation());
 }
index c02ce9b728d53d0e62fd702e6d9ac928083e3173..0d83a9bcc3af3ff5cc23601a5484b956785211cc 100644 (file)
@@ -224,6 +224,8 @@ int is_member_function_pointer3[
 int is_member_function_pointer4[
            is_member_function_pointer<int (X::*)(float) const>::value? 1 : -1];
 
+// Test substitution of non-dependent arguments back into the template
+// argument list of the class template partial specialization.
 template<typename T, typename ValueType = T>
 struct is_nested_value_type_identity {
   static const bool value = false;
@@ -245,11 +247,56 @@ struct HasIdentityValueType {
 
 struct NoValueType { };
 
-// FIXME: Need substitution into the template arguments of the partial spec
-//int is_nested_value_type_identity0[
-//            is_nested_value_type_identity<HasValueType<int> >::value? -1 : 1];
+int is_nested_value_type_identity0[
+            is_nested_value_type_identity<HasValueType<int> >::value? -1 : 1];
 int is_nested_value_type_identity1[
           is_nested_value_type_identity<HasIdentityValueType>::value? 1 : -1];
 // FIXME: Enable when we have SFINAE support
-//int is_nested_value_type_identity0[
+//int is_nested_value_type_identity2[
 //                   is_nested_value_type_identity<NoValueType>::value? -1 : 1];
+
+// FIXME: The tests that follow are stress-tests for the substitution
+// of deduced template arguments into the template argument list of a
+// partial specialization. I believe that we'll need this code for
+// substitution into function templates, but note that the examples
+// below are ill-formed and should eventually be removed.
+template<typename T, int N>
+struct is_sizeof_T {
+  static const bool value = false;
+};
+
+template<typename T>
+struct is_sizeof_T<T, sizeof(T)> {
+  static const bool value = true;
+};
+
+int is_sizeof_T0[is_sizeof_T<int, sizeof(int)>::value? 1 : -1];
+int is_sizeof_T1[is_sizeof_T<int, sizeof(char)>::value? -1 : 1];
+
+template<typename T>
+struct HasStaticOfT {
+  static T value;
+  static T other_value;
+};
+
+struct DerivedStaticOfInt : HasStaticOfT<int> { };
+
+template<typename X, typename T, T *Ptr>
+struct is_static_int_val {
+  static const bool value = false;
+};
+
+template<typename X, typename T>
+struct is_static_int_val<X, T, &X::value> {
+  static const bool value = true;
+};
+
+int is_static_int_val0[
+                 is_static_int_val<HasStaticOfT<int>, int, 
+                                   &HasStaticOfT<int>::value>::value ? 1 : -1];
+int is_static_int_val1[
+            is_static_int_val<HasStaticOfT<int>, int, 
+                              &HasStaticOfT<int>::other_value>::value ? -1 : 1];
+int is_static_int_val2[
+                 is_static_int_val<DerivedStaticOfInt, int, 
+                                   &HasStaticOfT<int>::value>::value ? 1 : -1];