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; +};