From a0ab7ccd216184853052cec1bce7900cbdeed0b4 Mon Sep 17 00:00:00 2001
From: Jason Merrill <jason@redhat.com>
Date: Tue, 29 Aug 2017 15:40:14 -0400
Subject: [PATCH] Add immediate potential_constant_expression variants.

	* constexpr.c (potential_constant_expression_1): Add "now" parm.
	(is_constant_expression, require_constant_expression): New.
	(is_static_init_expression, is_nondependent_constant_expression)
	(is_nondependent_static_init_expression): Drop "potential".
	* except.c (build_must_not_throw_expr): Do type conversion on
	value-dependent argument.
	* pt.c, semantics.c, typeck2.c: Use variants without "potential".

From-SVN: r251423
---
 gcc/cp/ChangeLog                             |  8 ++
 gcc/cp/constexpr.c                           | 95 ++++++++++++++------
 gcc/cp/cp-tree.h                             |  8 +-
 gcc/cp/except.c                              | 18 ++--
 gcc/cp/pt.c                                  | 15 ++--
 gcc/cp/semantics.c                           |  2 +-
 gcc/cp/typeck2.c                             |  2 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-conv2.C | 15 ++++
 8 files changed, 120 insertions(+), 43 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-conv2.C

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 308e13abba7d..f91f6bebcb6f 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,13 @@
 2017-08-29  Jason Merrill  <jason@redhat.com>
 
+	* constexpr.c (potential_constant_expression_1): Add "now" parm.
+	(is_constant_expression, require_constant_expression): New.
+	(is_static_init_expression, is_nondependent_constant_expression)
+	(is_nondependent_static_init_expression): Drop "potential".
+	* except.c (build_must_not_throw_expr): Do type conversion on
+	value-dependent argument.
+	* pt.c, semantics.c, typeck2.c: Use variants without "potential".
+
 	Instantiate default arguments/member initializers once.
 	* init.c (get_nsdmi): Remember NSDMI instantiations.
 	* parser.c (inject_this_parameter): Be more picky about
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 29ba2c3dac2c..8cfc5a455c19 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1181,7 +1181,7 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
       return t;
     }
 
-  if (!potential_constant_expression (new_call))
+  if (!is_constant_expression (new_call))
     {
       if (!*non_constant_p && !ctx->quiet)
 	error ("%q+E is not a constant expression", new_call);
@@ -4861,7 +4861,7 @@ maybe_constant_value (tree t, tree decl)
 {
   tree r;
 
-  if (!potential_nondependent_constant_expression (t))
+  if (!is_nondependent_constant_expression (t))
     {
       if (TREE_OVERFLOW_P (t))
 	{
@@ -4929,7 +4929,7 @@ fold_non_dependent_expr (tree t)
      as two declarations of the same function, for example.  */
   if (processing_template_decl)
     {
-      if (potential_nondependent_constant_expression (t))
+      if (is_nondependent_constant_expression (t))
 	{
 	  processing_template_decl_sentinel s;
 	  t = instantiate_non_dependent_expr_internal (t, tf_none);
@@ -4982,7 +4982,7 @@ maybe_constant_init (tree t, tree decl)
     t = TREE_OPERAND (t, 1);
   if (TREE_CODE (t) == TARGET_EXPR)
     t = TARGET_EXPR_INITIAL (t);
-  if (!potential_nondependent_static_init_expression (t))
+  if (!is_nondependent_static_init_expression (t))
     /* Don't try to evaluate it.  */;
   else if (CONSTANT_CLASS_P (t))
     /* No evaluation needed.  */;
@@ -5025,7 +5025,9 @@ check_automatic_or_tls (tree ref)
 
 /* Return true if T denotes a potentially constant expression.  Issue
    diagnostic as appropriate under control of FLAGS.  If WANT_RVAL is true,
-   an lvalue-rvalue conversion is implied.
+   an lvalue-rvalue conversion is implied.  If NOW is true, we want to
+   consider the expression in the current context, independent of constexpr
+   substitution.
 
    C++0x [expr.const] used to say
 
@@ -5041,10 +5043,12 @@ check_automatic_or_tls (tree ref)
       not evaluated are not considered.   */
 
 static bool
-potential_constant_expression_1 (tree t, bool want_rval, bool strict,
+potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
 				 tsubst_flags_t flags)
 {
-#define RECUR(T,RV) potential_constant_expression_1 ((T), (RV), strict, flags)
+#define RECUR(T,RV) \
+  potential_constant_expression_1 ((T), (RV), strict, now, flags)
+
   enum { any = false, rval = true };
   int i;
   tree tmp;
@@ -5087,7 +5091,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case USERDEF_LITERAL:
       /* We can see a FIELD_DECL in a pointer-to-member expression.  */
     case FIELD_DECL:
-    case PARM_DECL:
     case RESULT_DECL:
     case USING_DECL:
     case USING_STMT:
@@ -5098,6 +5101,15 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case STATIC_ASSERT:
       return true;
 
+    case PARM_DECL:
+      if (now)
+	{
+	  if (flags & tf_error)
+	    error ("%qE is not a constant expression", t);
+	  return false;
+	}
+      return true;
+
     case AGGR_INIT_EXPR:
     case CALL_EXPR:
       /* -- an invocation of a function other than a constexpr function
@@ -5173,7 +5185,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 		    tree x = get_nth_callarg (t, 0);
 		    if (is_this_parameter (x))
 		      return true;
-		    else if (!RECUR (x, rval))
+		    /* Don't require an immediately constant value, as
+		       constexpr substitution might not use the value.  */
+		    bool sub_now = false;
+		    if (!potential_constant_expression_1 (x, rval, strict,
+							  sub_now, flags))
 		      return false;
 		    i = 1;
 		  }
@@ -5203,7 +5219,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 	       REFERENCE_TYPE and we might not even know if the parameter
 	       is a reference, so accept lvalue constants too.  */
 	    bool rv = processing_template_decl ? any : rval;
-	    if (!RECUR (x, rv))
+	    /* Don't require an immediately constant value, as constexpr
+	       substitution might not use the value of the argument.  */
+	    bool sub_now = false;
+	    if (!potential_constant_expression_1 (x, rv, strict,
+						  sub_now, flags))
 	      return false;
           }
         return true;
@@ -5759,7 +5779,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 	return RECUR (TREE_OPERAND (t, 1), want_rval);
       for (i = 1; i < 3; ++i)
 	if (potential_constant_expression_1 (TREE_OPERAND (t, i),
-					     want_rval, strict, tf_none))
+					     want_rval, strict, now, tf_none))
 	  return true;
       if (flags & tf_error)
 	error_at (loc, "expression %qE is not a constant expression", t);
@@ -5816,13 +5836,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 bool
 potential_constant_expression (tree t)
 {
-  return potential_constant_expression_1 (t, false, true, tf_none);
-}
-
-bool
-potential_static_init_expression (tree t)
-{
-  return potential_constant_expression_1 (t, false, false, tf_none);
+  return potential_constant_expression_1 (t, false, true, false, tf_none);
 }
 
 /* As above, but require a constant rvalue.  */
@@ -5830,7 +5844,7 @@ potential_static_init_expression (tree t)
 bool
 potential_rvalue_constant_expression (tree t)
 {
-  return potential_constant_expression_1 (t, true, true, tf_none);
+  return potential_constant_expression_1 (t, true, true, false, tf_none);
 }
 
 /* Like above, but complain about non-constant expressions.  */
@@ -5838,7 +5852,7 @@ potential_rvalue_constant_expression (tree t)
 bool
 require_potential_constant_expression (tree t)
 {
-  return potential_constant_expression_1 (t, false, true, tf_warning_or_error);
+  return potential_constant_expression_1 (t, false, true, false, tf_warning_or_error);
 }
 
 /* Cross product of the above.  */
@@ -5846,7 +5860,38 @@ require_potential_constant_expression (tree t)
 bool
 require_potential_rvalue_constant_expression (tree t)
 {
-  return potential_constant_expression_1 (t, true, true, tf_warning_or_error);
+  return potential_constant_expression_1 (t, true, true, false, tf_warning_or_error);
+}
+
+/* Like potential_constant_expression, but don't consider possible constexpr
+   substitution of the current function.  That is, PARM_DECL qualifies under
+   potential_constant_expression, but not here.
+
+   This is basically what you can check when any actual constant values might
+   be value-dependent.  */
+
+bool
+is_constant_expression (tree t)
+{
+  return potential_constant_expression_1 (t, false, true, true, tf_none);
+}
+
+/* Like above, but complain about non-constant expressions.  */
+
+bool
+require_constant_expression (tree t)
+{
+  return potential_constant_expression_1 (t, false, true, true,
+					  tf_warning_or_error);
+}
+
+/* Like is_constant_expression, but allow const variables that are not allowed
+   under constexpr rules.  */
+
+bool
+is_static_init_expression (tree t)
+{
+  return potential_constant_expression_1 (t, false, false, true, tf_none);
 }
 
 /* Returns true if T is a potential constant expression that is not
@@ -5854,11 +5899,11 @@ require_potential_rvalue_constant_expression (tree t)
    in a template.  */
 
 bool
-potential_nondependent_constant_expression (tree t)
+is_nondependent_constant_expression (tree t)
 {
   return (!type_unknown_p (t)
 	  && !BRACE_ENCLOSED_INITIALIZER_P (t)
-	  && potential_constant_expression (t)
+	  && is_constant_expression (t)
 	  && !instantiation_dependent_expression_p (t));
 }
 
@@ -5866,11 +5911,11 @@ potential_nondependent_constant_expression (tree t)
    instantiation-dependent.  */
 
 bool
-potential_nondependent_static_init_expression (tree t)
+is_nondependent_static_init_expression (tree t)
 {
   return (!type_unknown_p (t)
 	  && !BRACE_ENCLOSED_INITIALIZER_P (t)
-	  && potential_static_init_expression (t)
+	  && is_static_init_expression (t)
 	  && !instantiation_dependent_expression_p (t));
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 17e7aad08f68..f0eafb3c2770 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7325,11 +7325,13 @@ extern bool is_valid_constexpr_fn		(tree, bool);
 extern bool check_constexpr_ctor_body           (tree, tree, bool);
 extern tree ensure_literal_type_for_constexpr_object (tree);
 extern bool potential_constant_expression       (tree);
-extern bool potential_nondependent_constant_expression (tree);
-extern bool potential_nondependent_static_init_expression (tree);
-extern bool potential_static_init_expression    (tree);
+extern bool is_constant_expression (tree);
+extern bool is_nondependent_constant_expression (tree);
+extern bool is_nondependent_static_init_expression (tree);
+extern bool is_static_init_expression    (tree);
 extern bool potential_rvalue_constant_expression (tree);
 extern bool require_potential_constant_expression (tree);
+extern bool require_constant_expression (tree);
 extern bool require_potential_rvalue_constant_expression (tree);
 extern tree cxx_constant_value			(tree, tree = NULL_TREE);
 extern tree maybe_constant_value		(tree, tree = NULL_TREE);
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index b25b91b97bec..2ee7e97ea231 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -261,13 +261,19 @@ build_must_not_throw_expr (tree body, tree cond)
   if (!flag_exceptions)
     return body;
 
-  if (cond && !value_dependent_expression_p (cond))
+  if (!cond)
+    /* OK, unconditional.  */;
+  else
     {
-      cond = perform_implicit_conversion_flags (boolean_type_node, cond,
-						tf_warning_or_error,
-						LOOKUP_NORMAL);
-      cond = instantiate_non_dependent_expr (cond);
-      cond = cxx_constant_value (cond);
+      tree conv = NULL_TREE;
+      if (!type_dependent_expression_p (cond))
+	conv = perform_implicit_conversion_flags (boolean_type_node, cond,
+						  tf_warning_or_error,
+						  LOOKUP_NORMAL);
+      if (tree inst = instantiate_non_dependent_or_null (conv))
+	cond = cxx_constant_value (inst);
+      else
+	require_constant_expression (cond);
       if (integer_zerop (cond))
 	return body;
       else if (integer_onep (cond))
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index eec89dbce5b1..e34fe21cb159 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -5827,7 +5827,7 @@ instantiate_non_dependent_expr_sfinae (tree expr, tsubst_flags_t complain)
 
      as two declarations of the same function, for example.  */
   if (processing_template_decl
-      && potential_nondependent_constant_expression (expr))
+      && is_nondependent_constant_expression (expr))
     {
       processing_template_decl_sentinel s;
       expr = instantiate_non_dependent_expr_internal (expr, complain);
@@ -5851,7 +5851,7 @@ instantiate_non_dependent_or_null (tree expr)
     return NULL_TREE;
   if (processing_template_decl)
     {
-      if (!potential_nondependent_constant_expression (expr))
+      if (!is_nondependent_constant_expression (expr))
 	expr = NULL_TREE;
       else
 	{
@@ -6437,15 +6437,11 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
       && has_value_dependent_address (expr))
     /* If we want the address and it's value-dependent, don't fold.  */;
   else if (processing_template_decl
-	   && potential_nondependent_constant_expression (expr))
+	   && is_nondependent_constant_expression (expr))
     non_dep = true;
   if (error_operand_p (expr))
     return error_mark_node;
   expr_type = TREE_TYPE (expr);
-  if (TREE_CODE (type) == REFERENCE_TYPE)
-    expr = mark_lvalue_use (expr);
-  else
-    expr = mark_rvalue_use (expr);
 
   /* If the argument is non-dependent, perform any conversions in
      non-dependent context as well.  */
@@ -6493,6 +6489,11 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	}
     }
 
+  if (TREE_CODE (type) == REFERENCE_TYPE)
+    expr = mark_lvalue_use (expr);
+  else
+    expr = mark_rvalue_use (expr);
+
   /* HACK: Due to double coercion, we can get a
      NOP_EXPR<REFERENCE_TYPE>(ADDR_EXPR<POINTER_TYPE> (arg)) here,
      which is the tree that we built on the first call (see
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 14e680f39f5d..86ce9ce9461d 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -735,7 +735,7 @@ finish_if_stmt_cond (tree cond, tree if_stmt)
 {
   cond = maybe_convert_cond (cond);
   if (IF_STMT_CONSTEXPR_P (if_stmt)
-      && require_potential_rvalue_constant_expression (cond)
+      && is_constant_expression (cond)
       && !value_dependent_expression_p (cond))
     {
       cond = instantiate_non_dependent_expr (cond);
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 06c079e41be0..e9aca395911d 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -821,7 +821,7 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
 	  || (DECL_IN_AGGR_P (decl) && !DECL_VAR_DECLARED_INLINE_P (decl)))
 	{
 	  /* Diagnose a non-constant initializer for constexpr.  */
-	  if (!require_potential_constant_expression (value))
+	  if (!require_constant_expression (value))
 	    value = error_mark_node;
 	  else
 	    value = cxx_constant_value (value, decl);
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-conv2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-conv2.C
new file mode 100644
index 000000000000..c64fb3d2fa0c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-conv2.C
@@ -0,0 +1,15 @@
+// { dg-do compile { target c++11 } }
+
+template <int I> void f();
+
+struct A { constexpr operator int() { return 24; } };
+
+template <class T> constexpr void g(T t)
+{
+  f<t>();
+}
+
+int main()
+{
+  g(A());
+}
-- 
GitLab