]> granicus.if.org Git - clang/commitdiff
Add semantic checking for template arguments that correspond to
authorDouglas Gregor <dgregor@apple.com>
Wed, 11 Feb 2009 01:18:59 +0000 (01:18 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 11 Feb 2009 01:18:59 +0000 (01:18 +0000)
non-type template parameters that are references to functions or
pointers to member functions. Did a little bit of refactoring so that
these two cases, along with the handling of non-type template
parameters that are pointers to functions, are handled by the same
path.

Also, tweaked FixOverloadedFunctionReference to cope with member
function pointers. This is a necessary step for getting all of the fun
member pointer conversions working outside of template arguments, too.

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

lib/Sema/SemaOverload.cpp
lib/Sema/SemaTemplate.cpp
test/SemaTemplate/temp_arg_nontype.cpp

index 64c61e17ad18f876104d7a5e3755345772450925..e40455b5c6d5e0dd9ff8f898a85a649bd7e6009e 100644 (file)
@@ -3952,6 +3952,25 @@ void Sema::FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn) {
   } else if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(E)) {
     assert(UnOp->getOpcode() == UnaryOperator::AddrOf && 
            "Can only take the address of an overloaded function");
+    if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Fn)) {
+      if (Method->isStatic()) {
+        // Do nothing: static member functions aren't any different
+        // from non-member functions.
+      }
+      else if (QualifiedDeclRefExpr *DRE 
+                 = dyn_cast<QualifiedDeclRefExpr>(UnOp->getSubExpr())) {
+        // We have taken the address of a pointer to member
+        // function. Perform the computation here so that we get the
+        // appropriate pointer to member type.
+        DRE->setDecl(Fn);
+        DRE->setType(Fn->getType());
+        QualType ClassType
+          = Context.getTypeDeclType(cast<RecordDecl>(Method->getDeclContext()));
+        E->setType(Context.getMemberPointerType(Fn->getType(), 
+                                                ClassType.getTypePtr()));
+        return;
+      }
+    }
     FixOverloadedFunctionReference(UnOp->getSubExpr(), Fn);
     E->setType(Context.getPointerType(UnOp->getSubExpr()->getType()));
   } else if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) {
index d4ba0c5edba70646d021fc7162943428114309ae..eb7db25d6ef336d0a232842fb152d67c7da5fc1f 100644 (file)
@@ -896,58 +896,45 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
     return false;
   }
 
-  if (const PointerType *ParamPtrType = ParamType->getAsPointerType()) {
-    if (ParamPtrType->getPointeeType()->isObjectType()) {
-      // -- for a non-type template-parameter of type pointer to
-      //    object, qualification conversions (4.4) and the
-      //    array-to-pointer conversion (4.2) are applied.
-      if (ArgType->isArrayType()) {
-        ArgType = Context.getArrayDecayedType(ArgType);
-        ImpCastExprToType(Arg, ArgType);
-      }
-
-      if (IsQualificationConversion(ArgType, ParamType)) {
-        ArgType = ParamType;
-        ImpCastExprToType(Arg, ParamType);
-      }
-
-      if (!hasSameUnqualifiedType(ArgType, ParamType)) {
-        // We can't perform this conversion.
-        Diag(Arg->getSourceRange().getBegin(), 
-             diag::err_template_arg_not_convertible)
-          << Arg->getType() << Param->getType() << Arg->getSourceRange();
-        Diag(Param->getLocation(), diag::note_template_param_here);
-        return true;
-      }
-
-      // FIXME: Check the restrictions in p1!
-      return false;
-    }
-    
-    assert(ParamPtrType->getPointeeType()->isFunctionType() &&
-           "Only function pointers remain");
-    //   -- For a non-type template-parameter of type pointer to
-    //      function, only the function-to-pointer conversion (4.3) is
-    //      applied. If the template-argument represents a set of
-    //      overloaded functions (or a pointer to such), the matching
-    //      function is selected from the set (13.4).
-    if (hasSameUnqualifiedType(ArgType, ParamType)) {
+  // Handle pointer-to-function, reference-to-function, and
+  // pointer-to-member-function all in (roughly) the same way.
+  if (// -- For a non-type template-parameter of type pointer to
+      //    function, only the function-to-pointer conversion (4.3) is
+      //    applied. If the template-argument represents a set of
+      //    overloaded functions (or a pointer to such), the matching
+      //    function is selected from the set (13.4).
+      (ParamType->isPointerType() &&
+       ParamType->getAsPointerType()->getPointeeType()->isFunctionType()) ||
+      // -- For a non-type template-parameter of type reference to
+      //    function, no conversions apply. If the template-argument
+      //    represents a set of overloaded functions, the matching
+      //    function is selected from the set (13.4).
+      (ParamType->isReferenceType() &&
+       ParamType->getAsReferenceType()->getPointeeType()->isFunctionType()) ||
+      // -- For a non-type template-parameter of type pointer to
+      //    member function, no conversions apply. If the
+      //    template-argument represents a set of overloaded member
+      //    functions, the matching member function is selected from
+      //    the set (13.4).
+      (ParamType->isMemberPointerType() &&
+       ParamType->getAsMemberPointerType()->getPointeeType()
+         ->isFunctionType())) {
+    if (hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) {
       // We don't have to do anything: the types already match.
-    }
-    else if (ArgType->isFunctionType()) {
+    } else if (ArgType->isFunctionType() && ParamType->isPointerType()) {
       ArgType = Context.getPointerType(ArgType);
       ImpCastExprToType(Arg, ArgType);
     } else if (FunctionDecl *Fn 
                  = ResolveAddressOfOverloadedFunction(Arg, ParamType, true)) {
       FixOverloadedFunctionReference(Arg, Fn);
       ArgType = Arg->getType();
-      if (ArgType->isFunctionType()) {
+      if (ArgType->isFunctionType() && ParamType->isPointerType()) {
         ArgType = Context.getPointerType(Arg->getType());
         ImpCastExprToType(Arg, ArgType);
       }
     }
 
-    if (!hasSameUnqualifiedType(ArgType, ParamType)) {
+    if (!hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) {
       // We can't perform this conversion.
       Diag(Arg->getSourceRange().getBegin(), 
            diag::err_template_arg_not_convertible)
@@ -960,43 +947,73 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
     return false;
   }
 
+  if (const PointerType *ParamPtrType = ParamType->getAsPointerType()) {
+    //   -- for a non-type template-parameter of type pointer to
+    //      object, qualification conversions (4.4) and the
+    //      array-to-pointer conversion (4.2) are applied.
+    assert(ParamPtrType->getPointeeType()->isObjectType() &&
+           "Only object pointers allowed here");
+
+    if (ArgType->isArrayType()) {
+      ArgType = Context.getArrayDecayedType(ArgType);
+      ImpCastExprToType(Arg, ArgType);
+    }
+    
+    if (IsQualificationConversion(ArgType, ParamType)) {
+      ArgType = ParamType;
+      ImpCastExprToType(Arg, ParamType);
+    }
+    
+    if (!hasSameUnqualifiedType(ArgType, ParamType)) {
+      // We can't perform this conversion.
+      Diag(Arg->getSourceRange().getBegin(), 
+           diag::err_template_arg_not_convertible)
+        << Arg->getType() << Param->getType() << Arg->getSourceRange();
+      Diag(Param->getLocation(), diag::note_template_param_here);
+      return true;
+    }
+    
+    // FIXME: Check the restrictions in p1!
+    return false;
+  }
+    
   if (const ReferenceType *ParamRefType = ParamType->getAsReferenceType()) {
-    if (ParamRefType->getPointeeType()->isObjectType()) {
-      // -- For a non-type template-parameter of type reference to
-      //    object, no conversions apply. The type referred to by the
-      //    reference may be more cv-qualified than the (otherwise
-      //    identical) type of the template-argument. The
-      //    template-parameter is bound directly to the
-      //    template-argument, which must be an lvalue.
-      if (!hasSameUnqualifiedType(ParamRefType->getPointeeType(), ArgType)) {
-        Diag(Arg->getSourceRange().getBegin(), 
-             diag::err_template_arg_no_ref_bind)
-          << Param->getType() << Arg->getType()
-          << Arg->getSourceRange();
-        Diag(Param->getLocation(), diag::note_template_param_here);
-        return true;
-      }
-
-      unsigned ParamQuals 
-        = Context.getCanonicalType(ParamType).getCVRQualifiers();
-      unsigned ArgQuals = Context.getCanonicalType(ArgType).getCVRQualifiers();
-        
-      if ((ParamQuals | ArgQuals) != ParamQuals) {
-        Diag(Arg->getSourceRange().getBegin(),
-             diag::err_template_arg_ref_bind_ignores_quals)
-          << Param->getType() << Arg->getType()
-          << Arg->getSourceRange();
-        Diag(Param->getLocation(), diag::note_template_param_here);
-        return true;
-      }
+    //   -- For a non-type template-parameter of type reference to
+    //      object, no conversions apply. The type referred to by the
+    //      reference may be more cv-qualified than the (otherwise
+    //      identical) type of the template-argument. The
+    //      template-parameter is bound directly to the
+    //      template-argument, which must be an lvalue.
+    assert(ParamRefType->getPointeeType()->isObjectType() &&
+           "Only object references allowed here");
+
+    if (!hasSameUnqualifiedType(ParamRefType->getPointeeType(), ArgType)) {
+      Diag(Arg->getSourceRange().getBegin(), 
+           diag::err_template_arg_no_ref_bind)
+        << Param->getType() << Arg->getType()
+        << Arg->getSourceRange();
+      Diag(Param->getLocation(), diag::note_template_param_here);
+      return true;
+    }
 
-      // FIXME: Check the restrictions in p1!
-      // CheckAddressConstantExpression(Lvalue) can be modified to do
-      // this.
-      return false;
+    unsigned ParamQuals 
+      = Context.getCanonicalType(ParamType).getCVRQualifiers();
+    unsigned ArgQuals = Context.getCanonicalType(ArgType).getCVRQualifiers();
+    
+    if ((ParamQuals | ArgQuals) != ParamQuals) {
+      Diag(Arg->getSourceRange().getBegin(),
+           diag::err_template_arg_ref_bind_ignores_quals)
+        << Param->getType() << Arg->getType()
+        << Arg->getSourceRange();
+      Diag(Param->getLocation(), diag::note_template_param_here);
+      return true;
     }
+    
+    // FIXME: Check the restrictions in p1!
+    // CheckAddressConstantExpression(Lvalue) can be modified to do
+    // this.
+    return false;
   }
-
   // FIXME: p5 has a lot more checks to perform!
 
   return false;
index 26ebb551e02078d61ffa2dffe9c8c0e3dea5695e..f069accb63b5a7d01f6bf919838d1f166e42828c 100644 (file)
@@ -79,3 +79,26 @@ A4<*X_volatile_ptr> *a15_2; // expected-error{{reference binding of non-type tem
 A4<y> *15_3; //  expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}}\
                   // FIXME: expected-error{{expected unqualified-id}}
 
+template<int (&fr)(int)> struct A5; // expected-note 2{{template parameter is declared here}}
+A5<h> *a16_1;
+A5<(h)> *a16_2;
+A5<f> *a16_3;
+A5<(f)> *a16_4;
+A5<h2> *a16_6;  // expected-error{{non-type template argument of type 'float (float)' cannot be converted to a value of type 'int (&)(int)'}} \
+// FIXME: expected-error{{expected unqualified-id}}
+A5<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (&)(int)'}}\
+// FIXME: expected-error{{expected unqualified-id}}
+// FIXME: the first error includes the string <overloaded function
+// type>, which makes Doug slightly unhappy.
+
+struct Z {
+  int foo(int);
+  float bar(float);
+  int bar(int);
+  double baz(double);
+};
+template<int (Z::*pmf)(int)> struct A6; // expected-note{{template parameter is declared here}}
+A6<&Z::foo> *a17_1;
+A6<&Z::bar> *a17_2;
+A6<&Z::baz> *a17_3; // expected-error{{non-type template argument of type 'double (struct Z::*)(double)' cannot be converted to a value of type 'int (struct Z::*)(int)'}} \
+// FIXME: expected-error{{expected unqualified-id}}