C++ PATCH for more N4268 bits, constant evaluation of all non-type args

Jason Merrill jason@redhat.com
Sat Jun 10 00:39:00 GMT 2017


My earlier patch for N4268 didn't implement the notion of converted
constant-expression, and as a result we weren't properly using
constexpr user-defined conversions to produce values for template
arguments.

Always performing these conversions led to some issues with
convert_nontype_argument getting confused by the trees built up to
express the conversion, especially for pointers to members, where we
would immediately decay the PTRMEM_CST to an INTEGER_CST even when
converting to the same type.  Changing that ended up requiring a
variety of other changes in pointer-to-member handling.

Tested x86_64-pc-linux-gnu, applying to trunk.
-------------- next part --------------
commit 6cc0f94b412830c68f349f88c4c1384efd880fc3
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jun 6 18:25:26 2017 -0700

    Missing bits from N4268, constant evaluation for all non-type args.
    
            * call.c (build_converted_constant_expr): Rename from
            build_integral_nontype_arg_conv, handle all types.
            * pt.c (convert_nontype_argument): In C++17 call it for all types.
            Move NOP stripping inside pointer case, don't strip ADDR_EXPR.
            * cvt.c (strip_fnptr_conv): Also strip conversions to the same type.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index a4b6a95..ef99683 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4005,15 +4005,16 @@ build_user_type_conversion (tree totype, tree expr, int flags,
 
 /* Subroutine of convert_nontype_argument.
 
-   EXPR is an argument for a template non-type parameter of integral or
-   enumeration type.  Do any necessary conversions (that are permitted for
-   non-type arguments) to convert it to the parameter type.
+   EXPR is an expression used in a context that requires a converted
+   constant-expression, such as a template non-type parameter.  Do any
+   necessary conversions (that are permitted for converted
+   constant-expressions) to convert it to the desired type.
 
    If conversion is successful, returns the converted expression;
    otherwise, returns error_mark_node.  */
 
 tree
-build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
+build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain)
 {
   conversion *conv;
   void *p;
@@ -4023,8 +4024,6 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
   if (error_operand_p (expr))
     return error_mark_node;
 
-  gcc_assert (INTEGRAL_OR_ENUMERATION_TYPE_P (type));
-
   /* Get the high-water mark for the CONVERSION_OBSTACK.  */
   p = conversion_obstack_alloc (0);
 
@@ -4032,36 +4031,86 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
 			      /*c_cast_p=*/false,
 			      LOOKUP_IMPLICIT, complain);
 
-  /* for a non-type template-parameter of integral or
-     enumeration type, integral promotions (4.5) and integral
-     conversions (4.7) are applied.  */
-  /* It should be sufficient to check the outermost conversion step, since
-     there are no qualification conversions to integer type.  */
-  if (conv)
-    switch (conv->kind)
-      {
-	/* A conversion function is OK.  If it isn't constexpr, we'll
-	   complain later that the argument isn't constant.  */
-      case ck_user:
-	/* The lvalue-to-rvalue conversion is OK.  */
-      case ck_rvalue:
-      case ck_identity:
-	break;
+  /* A converted constant expression of type T is an expression, implicitly
+     converted to type T, where the converted expression is a constant
+     expression and the implicit conversion sequence contains only
+
+       * user-defined conversions,
+       * lvalue-to-rvalue conversions (7.1),
+       * array-to-pointer conversions (7.2),
+       * function-to-pointer conversions (7.3),
+       * qualification conversions (7.5),
+       * integral promotions (7.6),
+       * integral conversions (7.8) other than narrowing conversions (11.6.4),
+       * null pointer conversions (7.11) from std::nullptr_t,
+       * null member pointer conversions (7.12) from std::nullptr_t, and
+       * function pointer conversions (7.13),
+
+     and where the reference binding (if any) binds directly.  */
+
+  for (conversion *c = conv;
+       conv && c->kind != ck_identity;
+       c = next_conversion (c))
+    {
+      switch (c->kind)
+	{
+	  /* A conversion function is OK.  If it isn't constexpr, we'll
+	     complain later that the argument isn't constant.  */
+	case ck_user:
+	  /* The lvalue-to-rvalue conversion is OK.  */
+	case ck_rvalue:
+	  /* Array-to-pointer and function-to-pointer.  */
+	case ck_lvalue:
+	  /* Function pointer conversions.  */
+	case ck_fnptr:
+	  /* Qualification conversions.  */
+	case ck_qual:
+	  break;
 
-      case ck_std:
-	t = next_conversion (conv)->type;
-	if (INTEGRAL_OR_ENUMERATION_TYPE_P (t))
+	case ck_ref_bind:
+	  if (c->need_temporary_p)
+	    {
+	      if (complain & tf_error)
+		error_at (loc, "initializing %qH with %qI in converted "
+			  "constant expression does not bind directly",
+			  type, next_conversion (c)->type);
+	      conv = NULL;
+	    }
 	  break;
 
-	if (complain & tf_error)
-	  error_at (loc, "conversion from %qH to %qI not considered for "
-		    "non-type template argument", t, type);
-	/* fall through.  */
+	case ck_base:
+	case ck_pmem:
+	case ck_ptr:
+	case ck_std:
+	  t = next_conversion (c)->type;
+	  if (INTEGRAL_OR_ENUMERATION_TYPE_P (t)
+	      && INTEGRAL_OR_ENUMERATION_TYPE_P (type))
+	    /* Integral promotion or conversion.  */
+	    break;
+	  if (NULLPTR_TYPE_P (t))
+	    /* Conversion from nullptr to pointer or pointer-to-member.  */
+	    break;
 
-      default:
-	conv = NULL;
-	break;
-      }
+	  if (complain & tf_error)
+	    error_at (loc, "conversion from %qH to %qI in a "
+		      "converted constant expression", t, type);
+	  /* fall through.  */
+
+	default:
+	  conv = NULL;
+	  break;
+	}
+    }
+
+  /* Avoid confusing convert_nontype_argument by introducing
+     a redundant conversion to the same reference type.  */
+  if (conv && conv->kind == ck_ref_bind
+      && REFERENCE_REF_P (expr))
+    {
+      tree ref = TREE_OPERAND (expr, 0);
+      if (same_type_p (type, TREE_TYPE (ref)))
+	return ref;
+    }
 
   if (conv)
     expr = convert_like (conv, expr, complain);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 07da0cd..6d4d937 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5907,7 +5907,7 @@ extern bool reference_related_p			(tree, tree);
 extern int remaining_arguments			(tree);
 extern tree perform_implicit_conversion		(tree, tree, tsubst_flags_t);
 extern tree perform_implicit_conversion_flags	(tree, tree, tsubst_flags_t, int);
-extern tree build_integral_nontype_arg_conv	(tree, tree, tsubst_flags_t);
+extern tree build_converted_constant_expr	(tree, tree, tsubst_flags_t);
 extern tree perform_direct_initialization_if_possible (tree, tree, bool,
                                                        tsubst_flags_t);
 extern tree in_charge_arg_for_name		(tree);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 3460e13..631ff49 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -2020,8 +2020,8 @@ fnptr_conv_p (tree to, tree from)
 	  || can_convert_tx_safety (t, f));
 }
 
-/* Return FN with any NOP_EXPRs that represent function pointer
-   conversions stripped.  */
+/* Return FN with any NOP_EXPRs stripped that represent function pointer
+   conversions or conversions to the same type.  */
 
 tree
 strip_fnptr_conv (tree fn)
@@ -2029,7 +2029,10 @@ strip_fnptr_conv (tree fn)
   while (TREE_CODE (fn) == NOP_EXPR)
     {
       tree op = TREE_OPERAND (fn, 0);
-      if (fnptr_conv_p (TREE_TYPE (fn), TREE_TYPE (op)))
+      tree ft = TREE_TYPE (fn);
+      tree ot = TREE_TYPE (op);
+      if (same_type_p (ft, ot)
+	  || fnptr_conv_p (ft, ot))
 	fn = op;
       else
 	break;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b537cb8..4d4484f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -6430,6 +6430,8 @@ static tree
 convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 {
   tree expr_type;
+  location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+  tree orig_expr = expr;
 
   /* Detect immediately string literals as invalid non-type argument.
      This special-case is not needed for correctness (we would easily
@@ -6503,18 +6505,17 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	   argument for a parameter of pointer to member type, we just want
 	   to leave it in that form rather than lower it to a
 	   CONSTRUCTOR.  */;
-      else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
-	/* Constant value checking is done later with type conversion.  */;
-      else if (cxx_dialect >= cxx1z)
+      else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)
+	       || cxx_dialect >= cxx1z)
 	{
-	  if (TREE_CODE (type) != REFERENCE_TYPE)
-	    expr = maybe_constant_value (expr);
-	  else if (REFERENCE_REF_P (expr))
-	    {
-	      expr = TREE_OPERAND (expr, 0);
-	      expr = maybe_constant_value (expr);
-	      expr = convert_from_reference (expr);
-	    }
+	  /* C++17: A template-argument for a non-type template-parameter shall
+	     be a converted constant expression (8.20) of the type of the
+	     template-parameter.  */
+	  expr = build_converted_constant_expr (type, expr, complain);
+	  if (expr == error_mark_node)
+	    return error_mark_node;
+	  expr = maybe_constant_value (expr);
+	  expr = convert_from_reference (expr);
 	}
       else if (TYPE_PTR_OR_PTRMEM_P (type))
 	{
@@ -6558,26 +6559,6 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	}
     }
 
-  /* We could also generate a NOP_EXPR(ADDR_EXPR()) when the
-     parameter is a pointer to object, through decay and
-     qualification conversion. Let's strip everything.  */
-  else if (TREE_CODE (expr) == NOP_EXPR && TYPE_PTROBV_P (type))
-    {
-      tree probe = expr;
-      STRIP_NOPS (probe);
-      if (TREE_CODE (probe) == ADDR_EXPR
-	  && TYPE_PTR_P (TREE_TYPE (probe)))
-	{
-	  /* Skip the ADDR_EXPR only if it is part of the decay for
-	     an array. Otherwise, it is part of the original argument
-	     in the source code.  */
-	  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (probe, 0))) == ARRAY_TYPE)
-	    probe = TREE_OPERAND (probe, 0);
-	  expr = probe;
-	  expr_type = TREE_TYPE (expr);
-	}
-    }
-
   /* [temp.arg.nontype]/5, bullet 1
 
      For a non-type template-parameter of integral or enumeration type,
@@ -6585,10 +6566,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
      (_conv.integral_) are applied.  */
   if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
     {
-      tree t = build_integral_nontype_arg_conv (type, expr, complain);
-      t = maybe_constant_value (t);
-      if (t != error_mark_node)
-	expr = t;
+      if (cxx_dialect < cxx11)
+	{
+	  tree t = build_converted_constant_expr (type, expr, complain);
+	  t = maybe_constant_value (t);
+	  if (t != error_mark_node)
+	    expr = t;
+	}
 
       if (!same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (expr)))
 	return error_mark_node;
@@ -6606,8 +6590,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 		return NULL_TREE;
 	      expr = cxx_constant_value (expr);
 	      if (errorcount > errs || warningcount + werrorcount > warns)
-		inform (EXPR_LOC_OR_LOC (expr, input_location),
-			"in template argument for type %qT ", type);
+		inform (loc, "in template argument for type %qT ", type);
 	      if (expr == error_mark_node)
 		return NULL_TREE;
 	      /* else cxx_constant_value complained but gave us
@@ -6629,6 +6612,23 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
      conversion (_conv.array_) are applied.  */
   else if (TYPE_PTROBV_P (type))
     {
+      tree decayed = expr;
+
+      /* Look through any NOP_EXPRs around an ADDR_EXPR, whether they come from
+	 decay_conversion or an explicit cast.  If it's a problematic cast,
+	 we'll complain about it below.  */
+      if (TREE_CODE (expr) == NOP_EXPR)
+	{
+	  tree probe = expr;
+	  STRIP_NOPS (probe);
+	  if (TREE_CODE (probe) == ADDR_EXPR
+	      && TYPE_PTR_P (TREE_TYPE (probe)))
+	    {
+	      expr = probe;
+	      expr_type = TREE_TYPE (expr);
+	    }
+	}
+
       /* [temp.arg.nontype]/1  (TC1 version, DR 49):
 
 	 A template-argument for a non-type, non-template template-parameter
@@ -6648,15 +6648,14 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	;
       else if (cxx_dialect >= cxx11 && integer_zerop (expr))
 	/* Null pointer values are OK in C++11.  */;
-      else if (TREE_CODE (expr) != ADDR_EXPR
-	       && TREE_CODE (expr_type) != ARRAY_TYPE)
+      else if (TREE_CODE (expr) != ADDR_EXPR)
 	{
 	  if (VAR_P (expr))
 	    {
 	      if (complain & tf_error)
 		error ("%qD is not a valid template argument "
 		       "because %qD is a variable, not the address of "
-		       "a variable", expr, expr);
+		       "a variable", orig_expr, expr);
 	      return NULL_TREE;
 	    }
 	  if (POINTER_TYPE_P (expr_type))
@@ -6664,7 +6663,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	      if (complain & tf_error)
 		error ("%qE is not a valid template argument for %qT "
 		       "because it is not the address of a variable",
-		       expr, type);
+		       orig_expr, type);
 	      return NULL_TREE;
 	    }
 	  /* Other values, like integer constants, might be valid
@@ -6673,15 +6672,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	}
       else
 	{
-	  tree decl;
+	  tree decl = TREE_OPERAND (expr, 0);
 
-	  decl = ((TREE_CODE (expr) == ADDR_EXPR)
-		  ? TREE_OPERAND (expr, 0) : expr);
 	  if (!VAR_P (decl))
 	    {
 	      if (complain & tf_error)
 		error ("%qE is not a valid template argument of type %qT "
-		       "because %qE is not a variable", expr, type, decl);
+		       "because %qE is not a variable", orig_expr, type, decl);
 	      return NULL_TREE;
 	    }
 	  else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl))
@@ -6689,7 +6686,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	      if (complain & tf_error)
 		error ("%qE is not a valid template argument of type %qT "
 		       "because %qD does not have external linkage",
-		       expr, type, decl);
+		       orig_expr, type, decl);
 	      return NULL_TREE;
 	    }
 	  else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx1z)
@@ -6697,7 +6694,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	    {
 	      if (complain & tf_error)
 		error ("%qE is not a valid template argument of type %qT "
-		       "because %qD has no linkage", expr, type, decl);
+		       "because %qD has no linkage", orig_expr, type, decl);
 	      return NULL_TREE;
 	    }
 	  /* C++17: For a non-type template-parameter of reference or pointer
@@ -6734,9 +6731,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	    }
 	}
 
-      expr = decay_conversion (expr, complain);
-      if (expr == error_mark_node)
-	return error_mark_node;
+      expr = decayed;
 
       expr = perform_qualification_conversions (type, expr);
       if (expr == error_mark_node)
@@ -23858,7 +23853,7 @@ value_dependent_expression_p (tree expression)
 	      {
 		tree t = TREE_OPERAND (expression, i);
 
-		/* In some cases, some of the operands may be missing.l
+		/* In some cases, some of the operands may be missing.
 		   (For example, in the case of PREDECREMENT_EXPR, the
 		   amount to increment by may be missing.)  That doesn't
 		   make the expression dependent.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C
index b517114..98bb502 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C
@@ -10,4 +10,4 @@ struct B
 { };
 
 constexpr A a { };
-B<a> b;				// { dg-error "template argument|invalid type" }
+B<a> b;			 // { dg-error "template argument|converted constant" }
diff --git a/gcc/testsuite/g++.dg/template/crash106.C b/gcc/testsuite/g++.dg/template/crash106.C
index 5bef101..8d97269 100644
--- a/gcc/testsuite/g++.dg/template/crash106.C
+++ b/gcc/testsuite/g++.dg/template/crash106.C
@@ -11,4 +11,4 @@ template<T N = 0, void (A::*)() = &A::foo<N> > struct B {}; // { dg-error "type|
 
 B<> b; // { dg-message "non-type" }
 
-// { dg-prune-output "could not convert" }
+// { dg-prune-output "(could not convert|no matches)" }
diff --git a/gcc/testsuite/g++.dg/template/crash84.C b/gcc/testsuite/g++.dg/template/crash84.C
index 103e90a..b3099d9 100644
--- a/gcc/testsuite/g++.dg/template/crash84.C
+++ b/gcc/testsuite/g++.dg/template/crash84.C
@@ -14,7 +14,7 @@ void
 foo ()
 {
   a<int> a1; // OK
-  a<int>::b<a,int> b1; // { dg-error "template argument" }
+  a<int>::b<a,int> b1; // { dg-error "template argument|converted constant" }
 }
 
 // { dg-prune-output "invalid type in declaration" }
diff --git a/gcc/testsuite/g++.dg/template/crash87.C b/gcc/testsuite/g++.dg/template/crash87.C
index 7b8bf4a..af81edb 100644
--- a/gcc/testsuite/g++.dg/template/crash87.C
+++ b/gcc/testsuite/g++.dg/template/crash87.C
@@ -17,7 +17,7 @@ template <bool name>
 class BUG2 : BUG
 {
 public:
- typedef BUG1_5<name> ptr; // { dg-error "could not convert template argument" }
+ typedef BUG1_5<name> ptr; // { dg-error "convert" }
 };
 
 int main()
diff --git a/gcc/testsuite/g++.dg/template/dependent-args1.C b/gcc/testsuite/g++.dg/template/dependent-args1.C
index a540e55..8fffbf8 100644
--- a/gcc/testsuite/g++.dg/template/dependent-args1.C
+++ b/gcc/testsuite/g++.dg/template/dependent-args1.C
@@ -10,4 +10,4 @@ template<int N, void (A::*)() = &A::foo<N> > struct B {};
 
 B<int> b; // { dg-error "type/value mismatch|expected a constant|invalid type" }
 
-// { dg-prune-output "could not convert" }
+// { dg-prune-output "(could not convert|no match)" }
diff --git a/gcc/testsuite/g++.dg/template/nontype-array1.C b/gcc/testsuite/g++.dg/template/nontype-array1.C
index cf21908..f22551b 100644
--- a/gcc/testsuite/g++.dg/template/nontype-array1.C
+++ b/gcc/testsuite/g++.dg/template/nontype-array1.C
@@ -10,17 +10,31 @@ constexpr char const s3[] = "hi";  // OK since C++11
 
 constexpr char const * f() { return s3; }
 
+using fn_p = char const * (*)();
+template <fn_p> struct A { };
+constexpr fn_p f2() { return f; }
+
+struct B
+{
+  constexpr B() { }
+  constexpr operator const char *() { return s3; }
+};
+
 int main()
 {
   Message<s1> m1;  // OK (all versions)
   Message<s2> m2;  // OK for clang since C++14, for gcc since C++17
   Message<s3> m3;  // OK for clang/gcc since C++11
 
+  A<f2()> a1; // { dg-error "" "" { target c++14_down } }
+
   static char const s4[] = "hi";
   static constexpr char const s5[] = "hi";  // OK since C++11
   Message<s4> m4;  // { dg-error "no linkage" "" { target c++14_down } }
   Message<s5> m5;  // { dg-error "no linkage" "" { target c++14_down } }
   Message<f()> m6; // { dg-error "" "" { target c++14_down } }
+  Message<B{}> m7; // { dg-error "" "" { target c++14_down } }
+
 
   char const s8[] = "hi";
   Message<s8> m8;  // { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/template/nontype13.C b/gcc/testsuite/g++.dg/template/nontype13.C
index d604da9..3250109 100644
--- a/gcc/testsuite/g++.dg/template/nontype13.C
+++ b/gcc/testsuite/g++.dg/template/nontype13.C
@@ -11,7 +11,7 @@ struct Dummy
   template<bool B>
   void tester()
   {
-    bar<evil>()(); // { dg-error "constant|template" }
+    bar<evil>()(); // { dg-error "constant|template|convert" }
   }
   template<bool B>
   struct bar
diff --git a/gcc/testsuite/g++.dg/template/nontype21.C b/gcc/testsuite/g++.dg/template/nontype21.C
index 69cab54..508f909 100644
--- a/gcc/testsuite/g++.dg/template/nontype21.C
+++ b/gcc/testsuite/g++.dg/template/nontype21.C
@@ -4,4 +4,4 @@ template<char const * const x> class Something { };
 
 extern char const xyz;
 
-class SomethingElse:public Something<xyz> { }; // { dg-error "xyz. is a variable" }
+class SomethingElse:public Something<xyz> { }; // { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/template/nontype26.C b/gcc/testsuite/g++.dg/template/nontype26.C
index 763d987..588ce1c 100644
--- a/gcc/testsuite/g++.dg/template/nontype26.C
+++ b/gcc/testsuite/g++.dg/template/nontype26.C
@@ -7,7 +7,7 @@ template<int& i> void doit() {
 template<const int& i> class X {
 public:
     void foo() {
-      doit<i>();  // { dg-error "cv-qualification|no matching" }
+      doit<i>();  // { dg-error "qualifi|template arg|no matching" }
     }
 };
 
diff --git a/gcc/testsuite/g++.dg/template/ptrmem20.C b/gcc/testsuite/g++.dg/template/ptrmem20.C
index 2d2453d..e17ed84 100644
--- a/gcc/testsuite/g++.dg/template/ptrmem20.C
+++ b/gcc/testsuite/g++.dg/template/ptrmem20.C
@@ -12,5 +12,5 @@ template<void (A::*)()> void bar(); // { dg-message "note" }
 
 void baz()
 {
-  bar<&B::foo>();  // { dg-error "not a valid template argument|no match" }
+  bar<&B::foo>();  // { dg-error "template argument|no match" }
 }
diff --git a/gcc/testsuite/g++.dg/template/ptrmem8.C b/gcc/testsuite/g++.dg/template/ptrmem8.C
index 85ffa4a..b759b70 100644
--- a/gcc/testsuite/g++.dg/template/ptrmem8.C
+++ b/gcc/testsuite/g++.dg/template/ptrmem8.C
@@ -15,10 +15,8 @@ template <int (D::*fun)() const> int Get(); // { dg-message "note" }
 
 int main () 
 {
-  Get<&B::I>();   // { dg-error "not a valid template argument" "not valid" }
+  Get<&B::I>();   // { dg-error "template argument|converted constant" "not valid" }
   // { dg-error "no match" "no match" { target *-*-* } .-1 }
-  // { dg-message "note" "note" { target *-*-* } .-2 }
-  Get<&D::I>();   // { dg-error "not a valid template argument" "not valid" }
+  Get<&D::I>();   // { dg-error "template argument|converted constant" "not valid" }
   // { dg-error "no match" "no match" { target *-*-* } .-1 }
-  // { dg-message "note" "note" { target *-*-* } .-2 }
 }

commit 709f012d7340a8472fa40567c12a750cb7922e09
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Jun 9 16:16:20 2017 -0700

    Overhaul pointer-to-member conversion and template argument handling.
    
            * call.c (standard_conversion): Avoid creating ck_pmem when the
            class type is the same.
            * cvt.c (can_convert_qual): Split from
            perform_qualification_conversions.
            * constexpr.c (cxx_eval_constant_expression): Check it.
            * typeck.c (convert_ptrmem): Only cplus_expand_constant if
            adjustment is necessary.
            * pt.c (check_valid_ptrmem_cst_expr): Compare class types.
            (convert_nontype_argument): Avoid redundant error.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 5e65bfb..a4b6a95 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -1262,14 +1262,16 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
 	  tree fbase = TYPE_PTRMEM_CLASS_TYPE (from);
 	  tree tbase = TYPE_PTRMEM_CLASS_TYPE (to);
 
-	  if (DERIVED_FROM_P (fbase, tbase)
-	      && (same_type_ignoring_top_level_qualifiers_p
-		  (from_pointee, to_pointee)))
+	  if (same_type_p (fbase, tbase))
+	    /* No base conversion needed.  */;
+	  else if (DERIVED_FROM_P (fbase, tbase)
+		   && (same_type_ignoring_top_level_qualifiers_p
+		       (from_pointee, to_pointee)))
 	    {
 	      from = build_ptrmem_type (tbase, from_pointee);
 	      conv = build_conv (ck_pmem, from, conv);
 	    }
-	  else if (!same_type_p (fbase, tbase))
+	  else
 	    return NULL;
 	}
       else if (CLASS_TYPE_P (from_pointee)
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 8bbe950..ae24e40 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4399,7 +4399,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
 	  {
 	    if (same_type_ignoring_top_level_qualifiers_p (type,
-							   TREE_TYPE (op)))
+							   TREE_TYPE (op))
+		|| can_convert_qual (type, op))
 	      return cp_fold_convert (type, op);
 	    else
 	      {
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 550dbf2..07da0cd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6022,6 +6022,7 @@ extern tree convert_force			(tree, tree, int,
 						 tsubst_flags_t);
 extern tree build_expr_type_conversion		(int, tree, bool);
 extern tree type_promotes_to			(tree);
+extern bool can_convert_qual			(tree, tree);
 extern tree perform_qualification_conversions	(tree, tree);
 extern bool tx_safe_fn_type_p			(tree);
 extern tree tx_unsafe_fn_variant		(tree);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 6b28ef6..3460e13 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1890,6 +1890,26 @@ type_promotes_to (tree type)
    closely.  Although they are used only in pt.c at the moment, they
    should presumably be used everywhere in the future.  */
 
+/* True iff EXPR can be converted to TYPE via a qualification conversion.
+   Callers should check for identical types before calling this function.  */
+
+bool
+can_convert_qual (tree type, tree expr)
+{
+  tree expr_type = TREE_TYPE (expr);
+  gcc_assert (!same_type_p (type, expr_type));
+
+  if (TYPE_PTR_P (type) && TYPE_PTR_P (expr_type))
+    return comp_ptr_ttypes (TREE_TYPE (type), TREE_TYPE (expr_type));
+  else if (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (expr_type))
+    return (same_type_p (TYPE_PTRMEM_CLASS_TYPE (type),
+			 TYPE_PTRMEM_CLASS_TYPE (expr_type))
+	    && comp_ptr_ttypes (TYPE_PTRMEM_POINTED_TO_TYPE (type),
+				TYPE_PTRMEM_POINTED_TO_TYPE (expr_type)));
+  else
+    return false;
+}
+
 /* Attempt to perform qualification conversions on EXPR to convert it
    to TYPE.  Return the resulting expression, or error_mark_node if
    the conversion was impossible.  */
@@ -1903,14 +1923,7 @@ perform_qualification_conversions (tree type, tree expr)
 
   if (same_type_p (type, expr_type))
     return expr;
-  else if (TYPE_PTR_P (type) && TYPE_PTR_P (expr_type)
-	   && comp_ptr_ttypes (TREE_TYPE (type), TREE_TYPE (expr_type)))
-    return build_nop (type, expr);
-  else if (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (expr_type)
-	   && same_type_p (TYPE_PTRMEM_CLASS_TYPE (type),
-			   TYPE_PTRMEM_CLASS_TYPE (expr_type))
-	   && comp_ptr_ttypes (TYPE_PTRMEM_POINTED_TO_TYPE (type),
-			       TYPE_PTRMEM_POINTED_TO_TYPE (expr_type)))
+  else if (can_convert_qual (type, expr))
     return build_nop (type, expr);
   else
     return error_mark_node;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 40be3c1..b537cb8 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -6124,8 +6124,14 @@ static bool
 check_valid_ptrmem_cst_expr (tree type, tree expr,
 			     tsubst_flags_t complain)
 {
+  location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+  tree orig_expr = expr;
   STRIP_NOPS (expr);
-  if (expr && (null_ptr_cst_p (expr) || TREE_CODE (expr) == PTRMEM_CST))
+  if (null_ptr_cst_p (expr))
+    return true;
+  if (TREE_CODE (expr) == PTRMEM_CST
+      && same_type_p (TYPE_PTRMEM_CLASS_TYPE (type),
+		      PTRMEM_CST_CLASS (expr)))
     return true;
   if (cxx_dialect >= cxx11 && null_member_pointer_value_p (expr))
     return true;
@@ -6135,9 +6141,12 @@ check_valid_ptrmem_cst_expr (tree type, tree expr,
     return true;
   if (complain & tf_error)
     {
-      error ("%qE is not a valid template argument for type %qT",
-	     expr, type);
-      error ("it must be a pointer-to-member of the form %<&X::Y%>");
+      error_at (loc, "%qE is not a valid template argument for type %qT",
+		orig_expr, type);
+      if (TREE_CODE (expr) != PTRMEM_CST)
+	inform (loc, "it must be a pointer-to-member of the form %<&X::Y%>");
+      else
+	inform (loc, "because it is a member of %qT", PTRMEM_CST_CLASS (expr));
     }
   return false;
 }
@@ -6880,36 +6889,12 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
          expression must be a pointer-to-member constant.  */
       if (!value_dependent_expression_p (expr)
 	  && !check_valid_ptrmem_cst_expr (type, expr, complain))
-	return error_mark_node;
+	return NULL_TREE;
 
       /* Repeated conversion can't deal with a conversion that turns PTRMEM_CST
 	 into a CONSTRUCTOR, so build up a new PTRMEM_CST instead.  */
       if (fnptr_conv_p (type, TREE_TYPE (expr)))
 	expr = make_ptrmem_cst (type, PTRMEM_CST_MEMBER (expr));
-
-      /* There is no way to disable standard conversions in
-	 resolve_address_of_overloaded_function (called by
-	 instantiate_type). It is possible that the call succeeded by
-	 converting &B::I to &D::I (where B is a base of D), so we need
-	 to reject this conversion here.
-
-	 Actually, even if there was a way to disable standard conversions,
-	 it would still be better to reject them here so that we can
-	 provide a superior diagnostic.  */
-      if (!same_type_p (TREE_TYPE (expr), type))
-	{
-	  if (complain & tf_error)
-	    {
-	      error ("%qE is not a valid template argument for type %qT "
-		     "because it is of type %qT", expr, type,
-		     TREE_TYPE (expr));
-	      /* If we are just one standard conversion off, explain.  */
-	      if (can_convert_standard (type, TREE_TYPE (expr), complain))
-		inform (input_location,
-			"standard conversions are not allowed in this context");
-	    }
-	  return NULL_TREE;
-	}
     }
   /* [temp.arg.nontype]/5, bullet 7
 
@@ -6921,7 +6906,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
          expression must be a pointer-to-member constant.  */
       if (!value_dependent_expression_p (expr)
 	  && !check_valid_ptrmem_cst_expr (type, expr, complain))
-	return error_mark_node;
+	return NULL_TREE;
 
       expr = perform_qualification_conversions (type, expr);
       if (expr == error_mark_node)
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 334a6f5..34d475b 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -6710,12 +6710,13 @@ tree
 convert_ptrmem (tree type, tree expr, bool allow_inverse_p,
 		bool c_cast_p, tsubst_flags_t complain)
 {
+  if (same_type_p (type, TREE_TYPE (expr)))
+    return expr;
+
   if (TYPE_PTRDATAMEM_P (type))
     {
       tree delta;
 
-      if (TREE_CODE (expr) == PTRMEM_CST)
-	expr = cplus_expand_constant (expr);
       delta = get_delta_difference (TYPE_PTRMEM_CLASS_TYPE (TREE_TYPE (expr)),
 				    TYPE_PTRMEM_CLASS_TYPE (type),
 				    allow_inverse_p,
@@ -6727,6 +6728,8 @@ convert_ptrmem (tree type, tree expr, bool allow_inverse_p,
 	{
 	  tree cond, op1, op2;
 
+	  if (TREE_CODE (expr) == PTRMEM_CST)
+	    expr = cplus_expand_constant (expr);
 	  cond = cp_build_binary_op (input_location,
 				     EQ_EXPR,
 				     expr,


More information about the Gcc-patches mailing list