This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[C++ PATCH] [PR8271] Check cv-quals of methods while unifying


Hello,

the following testcase is invalid and incorrectly accepted by GCC right now:

------------------------------------------
struct A {
    void foo(void) const;
};

template <class T>
void bar(void (T::*)(void)) {}

int main() {
    bar(&A::foo);
}
------------------------------------------

I discussed the validity of this with Jason on IRC, and we agree that it is
indeed invalid. The relevant pass seems to be 9.3.1/3 where the standard says
the cv-qualifiers of member functions affect both the implicit pointer-to-this
in the argument list *and* the function type.

So, if we try to unify "void (A::*)(void) const" and "void (T::*)(void)", we
ultimately must try to unify the function types: the return type and argument
list types can be obviously unified (including the implicit this pointer: we
can deduce "const A*" from "T*" with T=const A), but the whole function type
cannot because of the cv-quals. In fact, the function type of &A::foo is "void
(*)(void) const", which is different from "void (*)(void)". Another way to
approacching this is with an obvious analogy with pointer-to-members:
unification of "const int A::*" and "int T::*" must obviously fail.

Currently, GCC unifies the function types using type_unification_real (plus
manually handling the return type). This handles only a part of the problem,
because unification of pointers-to-this with different cv-quals is allowed in
some cases (as already mentioned:  "T*" and "const A*" can be unified). So my
patch explicitly tests cv-quals of methods and makes the unification fails if
they don't match.

This patch has been tested on i686-pc-linux-gnu with no new regressions. I
discussed a little with Jason whether we want to patch
build_method_type_directly to copy the cv-quals within the METHOD_TYPE node.
This would avoid that ugly chain of macros in my patch (and other parts of the
frontend) to access them. I tried patching it but there were fallouts in
cp/mangle.c and similar, so I did not bother at this point.

OK for mainline?

Giovanni Bajo



cp/
        PR c++/8271
        * pt.c (unify) <METHOD_TYPE>: Check if cv-qualifiers match.

testsuite/
        PR c++/8271
        * g++.dg/template/ptrmem11.C: New test.


Index: pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.876
diff -c -3 -p -r1.876 pt.c
*** pt.c 26 Jun 2004 21:11:19 -0000 1.876
--- pt.c 7 Jul 2004 02:44:22 -0000
*************** unify (tree tparms, tree targs, tree par
*** 9961,9966 ****
--- 9970,10002 ----
        return 0;

      case METHOD_TYPE:
+       if (TREE_CODE (arg) != TREE_CODE (parm))
+  return 1;
+
+       /* CV qualifications for methods can never be deduced, they
+   must match exactly. Consider this:
+
+   struct A {
+      void foo(void) const;
+   }
+   template <class T> void bar(void (T::*)(void));
+
+   If we try to call "bar(&A::foo)", the call must fail (just
+   like we cannot unify "int A::*" with "const int A::*"). But
+   if we do not check explicitly for this situation, we would
+   call type_unification_real, which would try to unify the
+   implicit pointer-to-this in the parameter list of the argument
+   ("const A*") to the one in the parameter ("T*"). This would
+   succeed with "T = const A" (as we allow less qualifiers in
+   the template parameter), but it is wrong.  */
+       if (!check_cv_quals_for_unify
+      (strict_in,
+       TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (arg))),
+       TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (parm)))))
+  return 1;
+
+       /* fall-through.  */
+
      case FUNCTION_TYPE:
        if (TREE_CODE (arg) != TREE_CODE (parm))
   return 1;





// { dg-do compile }
// Origin: Wolfgang Bangerth <bangerth at ticam dot utexas dot edu>
//     and Rene Fonseca <fonseca at mip dot sdu dot dk>
// PR c++/8271: Check cv-qualifiers while unifying pointer to member
//  functions.

struct MyClass {
  void mMethod() throw() {}
  void cMethod() const throw() {}
  void vMethod() volatile throw() {}
  void cvMethod() const volatile throw() {}
};

template<class CLASS>
void mFunction(void (CLASS::* method)()) {}

template<class CLASS>
void cFunction(void (CLASS::* method)() const) {}

template<class CLASS>
void vFunction(void (CLASS::* method)() volatile) {}

template<class CLASS>
void cvFunction(void (CLASS::* method)() const volatile) {}

int main() {
  mFunction(&MyClass::mMethod);
  mFunction(&MyClass::cMethod);    // { dg-error "no matching function" }
  mFunction(&MyClass::vMethod);    // { dg-error "no matching function" }
  mFunction(&MyClass::cvMethod);   // { dg-error "no matching function" }

  cFunction(&MyClass::mMethod);    // { dg-error "no matching function" }
  cFunction(&MyClass::cMethod);
  cFunction(&MyClass::vMethod);    // { dg-error "no matching function" }
  cFunction(&MyClass::cvMethod);   // { dg-error "no matching function" }

  vFunction(&MyClass::mMethod);    // { dg-error "no matching function" }
  vFunction(&MyClass::cMethod);    // { dg-error "no matching function" }
  vFunction(&MyClass::vMethod);
  vFunction(&MyClass::cvMethod);   // { dg-error "no matching function" }

  cvFunction(&MyClass::mMethod);   // { dg-error "no matching function" }
  cvFunction(&MyClass::cMethod);   // { dg-error "no matching function" }
  cvFunction(&MyClass::vMethod);   // { dg-error "no matching function" }
  cvFunction(&MyClass::cvMethod);

  return 0;
}




Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]