This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH] [PR8271] Check cv-quals of methods while unifying
- From: "Giovanni Bajo" <giovannibajo at libero dot it>
- To: <gcc-patches at gcc dot gnu dot org>
- Cc: "Jason Merrill" <jason at redhat dot com>
- Date: Wed, 7 Jul 2004 05:33:50 +0200
- Subject: [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;
}