C++: Fix ICE in fold_for_warn on CAST_EXPR (PR c++/83974)

Message ID 1516717664-896-1-git-send-email-dmalcolm@redhat.com
State New
Headers show
Series
  • C++: Fix ICE in fold_for_warn on CAST_EXPR (PR c++/83974)
Related show

Commit Message

David Malcolm Jan. 23, 2018, 2:27 p.m.
PR c++/83974 reports an ICE within fold_for_warn when calling
cxx_eval_constant_expression on a CAST_EXPR.

This comes from a pointer-to-member-function.  The result of
build_ptrmemfunc (within cp_convert_to_pointer for a null ptr) is
a CONSTRUCTOR containing, amongst other things a CAST_EXPR of a
TREE_LIST containing the zero INTEGER_CST.

After r256804, fold_for_warn within a template calls
fold_non_dependent_expr.

For this tree, is_nondependent_constant_expression returns true.

potential_constant_expression_1 has these cases:

    case TREE_LIST:
      {
	gcc_assert (TREE_PURPOSE (t) == NULL_TREE
		    || DECL_P (TREE_PURPOSE (t)));
	if (!RECUR (TREE_VALUE (t), want_rval))
	  return false;
	if (TREE_CHAIN (t) == NULL_TREE)
	  return true;
	return RECUR (TREE_CHAIN (t), want_rval);
      }

and:

    case CAST_EXPR:
    case CONST_CAST_EXPR:
    case STATIC_CAST_EXPR:
    case REINTERPRET_CAST_EXPR:
    case IMPLICIT_CONV_EXPR:
      if (cxx_dialect < cxx11
	  && !dependent_type_p (TREE_TYPE (t))
	  && !INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (t)))
	/* In C++98, a conversion to non-integral type can't be part of a
	   constant expression.  */
	  {
	  // reject it
	  }
	// accept it

and thus returns true for the CAST_EXPR and TREE_LIST, and hence for the
CONSTRUCTOR as a whole.

However, cxx_eval_constant_expression does not support these tree codes,
ICEing in the default case with:

  internal_error ("unexpected expression %qE of kind %s", t,

It seems that, given potential_constant_expression_1 can return true for
these cases, then cxx_eval_constant_expression ought to handle them, which
this patch implements, fixing the ICE.

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu.
OK for trunk?

gcc/cp/ChangeLog:
	PR c++/83974
	* constexpr.c (cxx_eval_constant_expression): Handle case
	TREE_LIST.  Handle CAST_EXPR, CONST_CAST_EXPR, STATIC_CAST_EXPR,
	REINTERPRET_CAST_EXPR and IMPLICIT_CONV_EXPR via
	cxx_eval_unary_expression.

gcc/testsuite/ChangeLog:
	PR c++/83974
	* g++.dg/warn/pr83974.C: New test case.
---
 gcc/cp/constexpr.c                  | 27 +++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/warn/pr83974.C | 11 +++++++++++
 2 files changed, 38 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/warn/pr83974.C

-- 
1.8.5.3

Comments

Jason Merrill Jan. 23, 2018, 2:54 p.m. | #1
On Tue, Jan 23, 2018 at 9:27 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> PR c++/83974 reports an ICE within fold_for_warn when calling

> cxx_eval_constant_expression on a CAST_EXPR.

>

> This comes from a pointer-to-member-function.  The result of

> build_ptrmemfunc (within cp_convert_to_pointer for a null ptr) is

> a CONSTRUCTOR containing, amongst other things a CAST_EXPR of a

> TREE_LIST containing the zero INTEGER_CST.

>

> After r256804, fold_for_warn within a template calls

> fold_non_dependent_expr.

>

> For this tree, is_nondependent_constant_expression returns true.

>

> potential_constant_expression_1 has these cases:

>

>     case TREE_LIST:

>       {

>         gcc_assert (TREE_PURPOSE (t) == NULL_TREE

>                     || DECL_P (TREE_PURPOSE (t)));

>         if (!RECUR (TREE_VALUE (t), want_rval))

>           return false;

>         if (TREE_CHAIN (t) == NULL_TREE)

>           return true;

>         return RECUR (TREE_CHAIN (t), want_rval);

>       }

>

> and:

>

>     case CAST_EXPR:

>     case CONST_CAST_EXPR:

>     case STATIC_CAST_EXPR:

>     case REINTERPRET_CAST_EXPR:

>     case IMPLICIT_CONV_EXPR:

>       if (cxx_dialect < cxx11

>           && !dependent_type_p (TREE_TYPE (t))

>           && !INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (t)))

>         /* In C++98, a conversion to non-integral type can't be part of a

>            constant expression.  */

>           {

>           // reject it

>           }

>         // accept it

>

> and thus returns true for the CAST_EXPR and TREE_LIST, and hence for the

> CONSTRUCTOR as a whole.

>

> However, cxx_eval_constant_expression does not support these tree codes,


Because they are template-only codes, that
cxx_eval_constant_expression should never see.  They shouldn't survive
the call to instantiate_non_dependent_expr_internal from
fold_non_dependent_expr.

Jason

Patch

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index ca7f369..a593132 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4348,6 +4348,24 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       }
       break;
 
+    case TREE_LIST:
+      {
+	tree value, chain;
+	gcc_assert (TREE_PURPOSE (t) == NULL_TREE
+		    || DECL_P (TREE_PURPOSE (t)));
+	value = cxx_eval_constant_expression (ctx, TREE_VALUE (t), lval,
+					      non_constant_p, overflow_p,
+					      jump_target);
+	if (TREE_CHAIN (t))
+	  chain = cxx_eval_constant_expression (ctx, TREE_CHAIN (t), lval,
+						non_constant_p, overflow_p,
+						jump_target);
+	else
+	  chain = NULL_TREE;
+	r = tree_cons (TREE_PURPOSE (t), value, chain);
+      }
+      break;
+
     case POINTER_PLUS_EXPR:
     case POINTER_DIFF_EXPR:
     case PLUS_EXPR:
@@ -4594,6 +4612,15 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       return cxx_eval_statement_list (&new_ctx, t,
 				      non_constant_p, overflow_p, jump_target);
 
+    case CAST_EXPR:
+    case CONST_CAST_EXPR:
+    case STATIC_CAST_EXPR:
+    case REINTERPRET_CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
+      r = cxx_eval_unary_expression (ctx, t, lval,
+				     non_constant_p, overflow_p);
+      break;
+
     case BIND_EXPR:
       return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
 					   lval,
diff --git a/gcc/testsuite/g++.dg/warn/pr83974.C b/gcc/testsuite/g++.dg/warn/pr83974.C
new file mode 100644
index 0000000..af12c2d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/pr83974.C
@@ -0,0 +1,11 @@ 
+// { dg-options "-Wtautological-compare" }
+
+struct A {
+  typedef void (A::*B) ();
+  operator B ();
+};
+template <typename>
+struct C {
+  void foo () { d == 0; }
+  A d;
+};