On Fri, Feb 08, 2019 at 05:37:00PM -0500, Jason Merrill wrote:
> On 2/8/19 12:21 PM, Marek Polacek wrote:
> > r256999 removed early bailout for pointer-to-member-function types, so we
> > now try to tsubst each element of a pointer-to-member-function CONSTRUCTOR.
> > 
> > That's fine but the problem here is that we end up converting a null pointer
> > to pointer-to-member-function type and that crashes in fold_convert:
> > 
> > 16035     case INTEGER_CST:
> > 16039       {
> > 16040         /* Instantiate any typedefs in the type.  */
> > 16041         tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> > 16042         r = fold_convert (type, t);
> > 
> > It seems obvious to use cp_fold_convert which handles TYPE_PTRMEM_P, but
> > that then ICEs too, the infamous "canonical types differ for identical 
> > types":
> > type is
> > 
> > struct
> > {
> >    void A::<T344> (struct A *) * __pfn;
> >    long int __delta;
> > }
> > 
> > and the type of "0" is "void A::<T344> (struct A *) *".  These types are
> > structurally equivalent but have different canonical types.  (What's up
> > with that, anyway?  It seems OK that the canonical type of the struct is
> > the struct itself and that the canonical type of the pointer is the pointer
> > itself.)
> > 
> > That could be handled in cp_fold_convert: add code to convert an 
> > integer_zerop to
> > TYPE_PTRMEMFUNC_P.  Unfortunately the 0 is not null_ptr_cst_p because it's 
> > got
> > a pointer type.
> > 
> > Or just don't bother substituting null_member_pointer_value_p and avoid the
> > above.
> > 
> > Bootstrapped/regtested on x86_64-linux, ok for trunk and 8?
> > 
> > 2019-02-08  Marek Polacek  <pola...@redhat.com>
> > 
> >     PR c++/89212 - ICE converting nullptr to pointer-to-member-function.
> >     * pt.c (tsubst_copy_and_build) <case CONSTRUCTOR>: Return early for
> >     null member pointer value.
> > 
> >     * g++.dg/cpp0x/nullptr40.C: New test.
> > 
> > diff --git gcc/cp/pt.c gcc/cp/pt.c
> > index b8fbf4046f0..acc2d8f1feb 100644
> > --- gcc/cp/pt.c
> > +++ gcc/cp/pt.c
> > @@ -19251,6 +19251,9 @@ tsubst_copy_and_build (tree t,
> >        looked up by digest_init.  */
> >     process_index_p = !(type && MAYBE_CLASS_TYPE_P (type));
> > +   if (null_member_pointer_value_p (t))
> > +     RETURN (t);
> 
> I would expect this to do the wrong thing if type is different from
> TREE_TYPE (t).  Can we get here for a dependent PMF type like T (A::*)()?
> If not, let's assert that they're the same.  Otherwise, maybe cp_convert
> (type, nullptr_node)?

Yup, that's a concern.  But I'm not seeing any ICEs with the assert added and a
dependent PMF as in the new testcase.  And it seems we get a conversion error
if the types of the PMFs don't match.  If I'm wrong, this would be easy to
fix anyway.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-02-11  Marek Polacek  <pola...@redhat.com>

        PR c++/89212 - ICE converting nullptr to pointer-to-member-function.
        * pt.c (tsubst_copy_and_build) <case CONSTRUCTOR>: Return early for
        null member pointer value.

        * g++.dg/cpp0x/nullptr40.C: New test.
        * g++.dg/cpp0x/nullptr41.C: New test.

diff --git gcc/cp/pt.c gcc/cp/pt.c
index b8fbf4046f0..2682c68dcfa 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -19251,6 +19251,12 @@ tsubst_copy_and_build (tree t,
           looked up by digest_init.  */
        process_index_p = !(type && MAYBE_CLASS_TYPE_P (type));
 
+       if (null_member_pointer_value_p (t))
+         {
+           gcc_assert (same_type_p (type, TREE_TYPE (t)));
+           RETURN (t);
+         }
+
        n = vec_safe_copy (CONSTRUCTOR_ELTS (t));
         newlen = vec_safe_length (n);
        FOR_EACH_VEC_SAFE_ELT (n, idx, ce)
diff --git gcc/testsuite/g++.dg/cpp0x/nullptr40.C 
gcc/testsuite/g++.dg/cpp0x/nullptr40.C
new file mode 100644
index 00000000000..21c188bdb5e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/nullptr40.C
@@ -0,0 +1,19 @@
+// PR c++/89212
+// { dg-do compile { target c++11 } }
+
+template <int, typename T> using enable_if_t = int;
+
+template<class X, void(X::*foo)() = nullptr>
+struct p
+{
+    template<void(X::*fun)() = foo, typename T = enable_if_t<nullptr == fun, 
int>>
+    p(T) { }
+    p() = default;
+};
+
+struct A
+{
+    p<A> i = 1;
+    void bar();
+    p<A, &A::bar> j;
+};
diff --git gcc/testsuite/g++.dg/cpp0x/nullptr41.C 
gcc/testsuite/g++.dg/cpp0x/nullptr41.C
new file mode 100644
index 00000000000..54e66af2095
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/nullptr41.C
@@ -0,0 +1,19 @@
+// PR c++/89212
+// { dg-do compile { target c++11 } }
+
+template <int, typename T> using enable_if_t = int;
+
+template<typename U, typename W, typename Y, class X, W(X::*foo)() = nullptr>
+struct p
+{
+    template<U(Y::*fun)() = foo, typename T = enable_if_t<nullptr == fun, int>>
+    p(T) { }
+    p() = default;
+};
+
+struct A
+{
+    p<void, void, A, A> i = 1;
+    void bar();
+    p<void, void, A, A, &A::bar> j;
+};

Reply via email to