r224456 - [c++1z] Fixes for generalized non-type template argument support: check for

Richard Smith richard-llvm at metafoo.co.uk
Wed Dec 17 12:42:37 PST 2014


Author: rsmith
Date: Wed Dec 17 14:42:37 2014
New Revision: 224456

URL: http://llvm.org/viewvc/llvm-project?rev=224456&view=rev
Log:
[c++1z] Fixes for generalized non-type template argument support: check for
exact type match for deduced template arguments, and be sure to produce correct
canonical TemplateArgument representations to enable correct redeclaration
matching.

Modified:
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=224456&r1=224455&r2=224456&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Dec 17 14:42:37 2014
@@ -651,12 +651,8 @@ Sema::CheckNonTypeTemplateParameterType(
   //   A non-type template-parameter of type "array of T" or
   //   "function returning T" is adjusted to be of type "pointer to
   //   T" or "pointer to function returning T", respectively.
-  else if (T->isArrayType())
-    // FIXME: Keep the type prior to promotion?
-    return Context.getArrayDecayedType(T);
-  else if (T->isFunctionType())
-    // FIXME: Keep the type prior to promotion?
-    return Context.getPointerType(T);
+  else if (T->isArrayType() || T->isFunctionType())
+    return Context.getDecayedType(T);
 
   Diag(Loc, diag::err_template_nontype_parm_bad_type)
     << T;
@@ -719,7 +715,8 @@ Decl *Sema::ActOnNonTypeTemplateParamete
       return Param;
 
     TemplateArgument Converted;
-    ExprResult DefaultRes = CheckTemplateArgument(Param, Param->getType(), Default, Converted);
+    ExprResult DefaultRes =
+        CheckTemplateArgument(Param, Param->getType(), Default, Converted);
     if (DefaultRes.isInvalid()) {
       Param->setInvalidDecl();
       return Param;
@@ -4742,24 +4739,43 @@ static bool CheckTemplateArgumentPointer
 ///
 /// This routine implements the semantics of C++ [temp.arg.nontype].
 /// If an error occurred, it returns ExprError(); otherwise, it
-/// returns the converted template argument. \p
-/// InstantiatedParamType is the type of the non-type template
-/// parameter after it has been instantiated.
+/// returns the converted template argument. \p ParamType is the
+/// type of the non-type template parameter after it has been instantiated.
 ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
-                                       QualType InstantiatedParamType, Expr *Arg,
+                                       QualType ParamType, Expr *Arg,
                                        TemplateArgument &Converted,
                                        CheckTemplateArgumentKind CTAK) {
   SourceLocation StartLoc = Arg->getLocStart();
 
   // If either the parameter has a dependent type or the argument is
   // type-dependent, there's nothing we can check now.
-  if (InstantiatedParamType->isDependentType() || Arg->isTypeDependent()) {
+  if (ParamType->isDependentType() || Arg->isTypeDependent()) {
     // FIXME: Produce a cloned, canonical expression?
     Converted = TemplateArgument(Arg);
     return Arg;
   }
 
-  QualType ParamType = InstantiatedParamType;
+  // We should have already dropped all cv-qualifiers by now.
+  assert(!ParamType.hasQualifiers() &&
+         "non-type template parameter type cannot be qualified");
+
+  if (CTAK == CTAK_Deduced &&
+      !Context.hasSameUnqualifiedType(ParamType, Arg->getType())) {
+    // C++ [temp.deduct.type]p17:
+    //   If, in the declaration of a function template with a non-type
+    //   template-parameter, the non-type template-parameter is used
+    //   in an expression in the function parameter-list and, if the
+    //   corresponding template-argument is deduced, the
+    //   template-argument type shall match the type of the
+    //   template-parameter exactly, except that a template-argument
+    //   deduced from an array bound may be of any integral type.
+    Diag(StartLoc, diag::err_deduced_non_type_template_arg_type_mismatch)
+      << Arg->getType().getUnqualifiedType()
+      << ParamType.getUnqualifiedType();
+    Diag(Param->getLocation(), diag::note_template_param_here);
+    return ExprError();
+  }
+
   if (getLangOpts().CPlusPlus1z) {
     // FIXME: We can do some limited checking for a value-dependent but not
     // type-dependent argument.
@@ -4777,15 +4793,17 @@ ExprResult Sema::CheckTemplateArgument(N
     if (ArgResult.isInvalid())
       return ExprError();
 
+    QualType CanonParamType = Context.getCanonicalType(ParamType);
+
     // Convert the APValue to a TemplateArgument.
     switch (Value.getKind()) {
     case APValue::Uninitialized:
       assert(ParamType->isNullPtrType());
-      Converted = TemplateArgument(ParamType, /*isNullPtr*/true);
+      Converted = TemplateArgument(CanonParamType, /*isNullPtr*/true);
       break;
     case APValue::Int:
       assert(ParamType->isIntegralOrEnumerationType());
-      Converted = TemplateArgument(Context, Value.getInt(), ParamType);
+      Converted = TemplateArgument(Context, Value.getInt(), CanonParamType);
       break;
     case APValue::MemberPointer: {
       assert(ParamType->isMemberPointerType());
@@ -4800,14 +4818,15 @@ ExprResult Sema::CheckTemplateArgument(N
       }
 
       auto *VD = const_cast<ValueDecl*>(Value.getMemberPointerDecl());
-      Converted = VD ? TemplateArgument(VD, ParamType)
-                     : TemplateArgument(ParamType, /*isNullPtr*/true);
+      Converted = VD ? TemplateArgument(VD, CanonParamType)
+                     : TemplateArgument(CanonParamType, /*isNullPtr*/true);
       break;
     }
     case APValue::LValue: {
       //   For a non-type template-parameter of pointer or reference type,
       //   the value of the constant expression shall not refer to
-      assert(ParamType->isPointerType() || ParamType->isReferenceType());
+      assert(ParamType->isPointerType() || ParamType->isReferenceType() ||
+             ParamType->isNullPtrType());
       // -- a temporary object
       // -- a string literal
       // -- the result of a typeid expression, or
@@ -4837,10 +4856,12 @@ ExprResult Sema::CheckTemplateArgument(N
           << Value.getAsString(Context, ParamType);
         return ExprError();
       }
-      assert((VD || ParamType->isPointerType()) &&
+      assert((VD || !ParamType->isReferenceType()) &&
              "null reference should not be a constant expression");
-      Converted = VD ? TemplateArgument(VD, ParamType)
-                     : TemplateArgument(ParamType, /*isNullPtr*/true);
+      assert((!VD || !ParamType->isNullPtrType()) &&
+             "non-null value of type nullptr_t?");
+      Converted = VD ? TemplateArgument(VD, CanonParamType)
+                     : TemplateArgument(CanonParamType, /*isNullPtr*/true);
       break;
     }
     case APValue::AddrLabelDiff:
@@ -4875,23 +4896,6 @@ ExprResult Sema::CheckTemplateArgument(N
     //      enumeration type, integral promotions (4.5) and integral
     //      conversions (4.7) are applied.
 
-    if (CTAK == CTAK_Deduced &&
-        !Context.hasSameUnqualifiedType(ParamType, Arg->getType())) {
-      // C++ [temp.deduct.type]p17:
-      //   If, in the declaration of a function template with a non-type
-      //   template-parameter, the non-type template-parameter is used
-      //   in an expression in the function parameter-list and, if the
-      //   corresponding template-argument is deduced, the
-      //   template-argument type shall match the type of the
-      //   template-parameter exactly, except that a template-argument
-      //   deduced from an array bound may be of any integral type.
-      Diag(StartLoc, diag::err_deduced_non_type_template_arg_type_mismatch)
-        << Arg->getType().getUnqualifiedType()
-        << ParamType.getUnqualifiedType();
-      Diag(Param->getLocation(), diag::note_template_param_here);
-      return ExprError();
-    }
-
     if (getLangOpts().CPlusPlus11) {
       // We can't check arbitrary value-dependent arguments.
       // FIXME: If there's no viable conversion to the template parameter type,
@@ -4969,9 +4973,8 @@ ExprResult Sema::CheckTemplateArgument(N
         return ExprError();
     }
 
-    // From here on out, all we care about are the unqualified forms
-    // of the parameter and argument types.
-    ParamType = ParamType.getUnqualifiedType();
+    // From here on out, all we care about is the unqualified form
+    // of the argument type.
     ArgType = ArgType.getUnqualifiedType();
 
     // Try to convert the argument to the parameter's type.
@@ -4988,7 +4991,7 @@ ExprResult Sema::CheckTemplateArgument(N
       // We can't perform this conversion.
       Diag(Arg->getLocStart(),
            diag::err_template_arg_not_convertible)
-        << Arg->getType() << InstantiatedParamType << Arg->getSourceRange();
+        << Arg->getType() << ParamType << Arg->getSourceRange();
       Diag(Param->getLocation(), diag::note_template_param_here);
       return ExprError();
     }

Modified: cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp?rev=224456&r1=224455&r2=224456&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp Wed Dec 17 14:42:37 2014
@@ -109,4 +109,42 @@ namespace PtrMem {
   static_assert(!is_same<Ae, Aedb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
   static_assert(!is_same<Aecb, Aedb>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}}
   static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
+
+  using An = A<int E::*, nullptr>;
+  using A0 = A<int E::*, (int E::*)0>;
+  static_assert(is_same<An, A0>);
+}
+
+namespace DeduceDifferentType {
+  template<int N> struct A {};
+  template<long N> int a(A<N>); // expected-note {{does not have the same type}}
+  int a_imp = a(A<3>()); // expected-error {{no matching function}}
+  int a_exp = a<3>(A<3>());
+
+  template<decltype(nullptr)> struct B {};
+  template<int *P> int b(B<P>); // expected-note {{could not match}} expected-note {{not implicitly convertible}}
+  int b_imp = b(B<nullptr>()); // expected-error {{no matching function}}
+  int b_exp = b<nullptr>(B<nullptr>()); // expected-error {{no matching function}}
+
+  struct X { constexpr operator int() { return 0; } } x;
+  template<X &> struct C {};
+  template<int N> int c(C<N>); // expected-note {{does not have the same type}} expected-note {{not implicitly convertible}}
+  int c_imp = c(C<x>()); // expected-error {{no matching function}}
+  int c_exp = c<x>(C<x>()); // expected-error {{no matching function}}
+
+  struct Z;
+  struct Y { constexpr operator Z&(); } y;
+  struct Z { constexpr operator Y&() { return y; } } z;
+  constexpr Y::operator Z&() { return z; }
+  template<Y &> struct D {};
+  template<Z &z> int d(D<z>); // expected-note {{does not have the same type}}
+  int d_imp = d(D<y>()); // expected-error {{no matching function}}
+  int d_exp = d<y>(D<y>());
+}
+
+namespace DeclMatch {
+  template<typename T, T> int f();
+  template<typename T> class X { friend int f<T, 0>(); static int n; };
+  template<typename T, T> int f() { return X<T>::n; }
+  int k = f<int, 0>(); // ok, friend
 }





More information about the cfe-commits mailing list