diff mbox series

C++ PATCH for c++/84978, ICE with NRVO

Message ID 20180320133205.GE3522@redhat.com
State New
Headers show
Series C++ PATCH for c++/84978, ICE with NRVO | expand

Commit Message

Marek Polacek March 20, 2018, 1:32 p.m. UTC
We started crashing on this test with r258592 which added cp_get_callee_fndecl
in <case AGGR_INIT_EXPR> in cp_genericize_r.

This ICE apparently depends on whether we perform NRVO or not.  If the size of
S is <=16B we pass it in registers and it compiles fine.  But if the size of S
is >16B, then we pass in memory, and we NRV-optimize.  That means that
s.fn ();
is turned by finalize_nrv into
<retval>.fn ();

Then the newly added call to cp_get_callee_fndecl calls maybe_constant_init,
which ends up evaluating <retval>, but it's not in the hash map, so we crash
here:
4111       /* We ask for an rvalue for the RESULT_DECL when indirecting
4112          through an invisible reference, or in named return value
4113          optimization.  */
4114       return (*ctx->values->get (t));

I thought we could be more careful and not blindly dereference the result
of get().  After all, it's not a problem here if the <retval> cannot be
evaluated to a constant.

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

2018-03-20  Marek Polacek  <polacek@redhat.com>

	PR c++/84978
	* constexpr.c (cxx_eval_constant_expression): Handle the case when
	a RESULT_DECL isn't in the hash map.

	* g++.dg/opt/nrv19.C: New test.


	Marek

Comments

Jason Merrill March 20, 2018, 3:55 p.m. UTC | #1
On Tue, Mar 20, 2018 at 9:32 AM, Marek Polacek <polacek@redhat.com> wrote:
> We started crashing on this test with r258592 which added cp_get_callee_fndecl
> in <case AGGR_INIT_EXPR> in cp_genericize_r.
>
> This ICE apparently depends on whether we perform NRVO or not.  If the size of
> S is <=16B we pass it in registers and it compiles fine.  But if the size of S
> is >16B, then we pass in memory, and we NRV-optimize.  That means that
> s.fn ();
> is turned by finalize_nrv into
> <retval>.fn ();
>
> Then the newly added call to cp_get_callee_fndecl calls maybe_constant_init,

Oops, I forgot that cp_get_callee_fndecl would call
maybe_constant_init, I was just using it to handle both CALL_EXPR and
AGGR_INIT_EXPR.  And in fact it looks like we don't really want that
for the other users, either.  I think I'll remove it.

> 2018-03-20  Marek Polacek  <polacek@redhat.com>
>
>         PR c++/84978
>         * constexpr.c (cxx_eval_constant_expression): Handle the case when
>         a RESULT_DECL isn't in the hash map.

But your patch is also good.  OK.

Jason
Jason Merrill March 20, 2018, 5:59 p.m. UTC | #2
On Tue, Mar 20, 2018 at 11:55 AM, Jason Merrill <jason@redhat.com> wrote:
> On Tue, Mar 20, 2018 at 9:32 AM, Marek Polacek <polacek@redhat.com> wrote:
>> We started crashing on this test with r258592 which added cp_get_callee_fndecl
>> in <case AGGR_INIT_EXPR> in cp_genericize_r.
>>
>> This ICE apparently depends on whether we perform NRVO or not.  If the size of
>> S is <=16B we pass it in registers and it compiles fine.  But if the size of S
>> is >16B, then we pass in memory, and we NRV-optimize.  That means that
>> s.fn ();
>> is turned by finalize_nrv into
>> <retval>.fn ();
>>
>> Then the newly added call to cp_get_callee_fndecl calls maybe_constant_init,
>
> Oops, I forgot that cp_get_callee_fndecl would call
> maybe_constant_init, I was just using it to handle both CALL_EXPR and
> AGGR_INIT_EXPR.  And in fact it looks like we don't really want that
> for the other users, either.  I think I'll remove it.

Thus.
commit 06d961cdae77d73332845eaa2ed8d1ea1867dc73
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Mar 20 08:51:18 2018 -0400

            PR c++/84978, ICE with NRVO.
    
            * cvt.c (cp_get_fndecl_from_callee): Add fold parameter.
            (cp_get_callee_fndecl_nofold): New.
            * cp-gimplify.c (cp_genericize_r): Use it instead.
            * call.c (check_self_delegation): Likewise.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 23d4f82a1a0..dbdb8d5812d 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8739,7 +8739,7 @@ check_self_delegation (tree ret)
 {
   if (TREE_CODE (ret) == TARGET_EXPR)
     ret = TARGET_EXPR_INITIAL (ret);
-  tree fn = cp_get_callee_fndecl (ret);
+  tree fn = cp_get_callee_fndecl_nofold (ret);
   if (fn && DECL_ABSTRACT_ORIGIN (fn) == current_function_decl)
     error ("constructor delegates to itself");
 }
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 332ff2bbb05..3edecf24c92 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1521,7 +1521,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 	 version is inlinable, a direct call to this version can be made
 	 otherwise the call should go through the dispatcher.  */
       {
-	tree fn = cp_get_callee_fndecl (stmt);
+	tree fn = cp_get_callee_fndecl_nofold (stmt);
 	if (fn && DECL_FUNCTION_VERSIONED (fn)
 	    && (current_function_decl == NULL
 		|| !targetm.target_option.can_inline_p (current_function_decl,
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 727822e36da..9befde3329d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6110,7 +6110,8 @@ extern tree cp_convert_and_check                (tree, tree, tsubst_flags_t);
 extern tree cp_fold_convert			(tree, tree);
 extern tree cp_get_callee			(tree);
 extern tree cp_get_callee_fndecl		(tree);
-extern tree cp_get_fndecl_from_callee		(tree);
+extern tree cp_get_callee_fndecl_nofold		(tree);
+extern tree cp_get_fndecl_from_callee		(tree, bool fold = true);
 extern tree convert_to_void			(tree, impl_conv_void,
                                  		 tsubst_flags_t);
 extern tree convert_force			(tree, tree, int,
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 40e7576f23c..9b53fa3067d 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -941,7 +941,7 @@ cp_get_callee (tree call)
    if we can.  */
 
 tree
-cp_get_fndecl_from_callee (tree fn)
+cp_get_fndecl_from_callee (tree fn, bool fold /* = true */)
 {
   if (fn == NULL_TREE)
     return fn;
@@ -951,7 +951,8 @@ cp_get_fndecl_from_callee (tree fn)
   if (type == unknown_type_node)
     return NULL_TREE;
   gcc_assert (POINTER_TYPE_P (type));
-  fn = maybe_constant_init (fn);
+  if (fold)
+    fn = maybe_constant_init (fn);
   STRIP_NOPS (fn);
   if (TREE_CODE (fn) == ADDR_EXPR)
     {
@@ -971,6 +972,14 @@ cp_get_callee_fndecl (tree call)
   return cp_get_fndecl_from_callee (cp_get_callee (call));
 }
 
+/* As above, but not using the constexpr machinery.  */
+
+tree
+cp_get_callee_fndecl_nofold (tree call)
+{
+  return cp_get_fndecl_from_callee (cp_get_callee (call), false);
+}
+
 /* Subroutine of convert_to_void.  Warn if we're discarding something with
    attribute [[nodiscard]].  */
diff mbox series

Patch

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 894bcd0bb3e..1f8ece89730 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -4111,7 +4111,15 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       /* We ask for an rvalue for the RESULT_DECL when indirecting
 	 through an invisible reference, or in named return value
 	 optimization.  */
-      return (*ctx->values->get (t));
+      if (tree *p = ctx->values->get (t))
+	return *p;
+      else
+	{
+	  if (!ctx->quiet)
+	    error ("%qE is not a constant expression", t);
+	  *non_constant_p = true;
+	}
+      break;
 
     case VAR_DECL:
       if (DECL_HAS_VALUE_EXPR_P (t))
diff --git gcc/testsuite/g++.dg/opt/nrv19.C gcc/testsuite/g++.dg/opt/nrv19.C
index e69de29bb2d..385593cc90c 100644
--- gcc/testsuite/g++.dg/opt/nrv19.C
+++ gcc/testsuite/g++.dg/opt/nrv19.C
@@ -0,0 +1,15 @@ 
+// PR c++/84978
+// { dg-do compile }
+
+struct S {
+  void (*fn)();
+  int a[10];
+};
+
+S
+foo ()
+{
+  S s;
+  s.fn ();
+  return s;
+}