c++: ICE with reference NSDMI [PR114854]

Message ID 20240509160404.50969-1-polacek@redhat.com
State New
Headers
Series c++: ICE with reference NSDMI [PR114854] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Testing passed

Commit Message

Marek Polacek May 9, 2024, 4:04 p.m. UTC
  Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
Here we crash on a cp_gimplify_expr/TARGET_EXPR assert:

      /* A TARGET_EXPR that expresses direct-initialization should have been
         elided by cp_gimplify_init_expr.  */
      gcc_checking_assert (!TARGET_EXPR_DIRECT_INIT_P (*expr_p));

the TARGET_EXPR in question is created for the NSDMI in:

  class Vector { int m_size; };
  struct S {
    const Vector &vec{};
  };

where we first need to create a Vector{} temporary, and then bind the
vec reference to it.  The temporary is represented by a TARGET_EXPR
and it cannot be elided.  When we create an object of type S, we get

  D.2848 = {.vec=(const struct Vector &) &TARGET_EXPR <D.2840, {.m_size=0}>}

where the TARGET_EXPR is no longer direct-initializing anything.

Perhaps we can clear the TARGET_EXPR_DIRECT_INIT_P flag when taking the
address of an TARGET_EXPR, like below.  I'm also clearing _ELIDING_P
even though that's not necessary to fix this ICE.

	PR c++/114854

gcc/cp/ChangeLog:

	* typeck.cc (cp_build_addr_expr_1) <case TARGET_EXPR>: Clear
	TARGET_EXPR_DIRECT_INIT_P and TARGET_EXPR_ELIDING_P.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1y/nsdmi-aggr22.C: New test.
---
 gcc/cp/typeck.cc                          |  7 +++++++
 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr22.C | 12 ++++++++++++
 2 files changed, 19 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr22.C


base-commit: 80d1e2ec4d394111ebd50d2e8928f7596b7b5c7e
  

Comments

Jason Merrill May 9, 2024, 7:47 p.m. UTC | #1
On 5/9/24 12:04, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> -- >8 --
> Here we crash on a cp_gimplify_expr/TARGET_EXPR assert:
> 
>        /* A TARGET_EXPR that expresses direct-initialization should have been
>           elided by cp_gimplify_init_expr.  */
>        gcc_checking_assert (!TARGET_EXPR_DIRECT_INIT_P (*expr_p));
> 
> the TARGET_EXPR in question is created for the NSDMI in:
> 
>    class Vector { int m_size; };
>    struct S {
>      const Vector &vec{};
>    };
> 
> where we first need to create a Vector{} temporary, and then bind the
> vec reference to it.  The temporary is represented by a TARGET_EXPR
> and it cannot be elided.  When we create an object of type S, we get
> 
>    D.2848 = {.vec=(const struct Vector &) &TARGET_EXPR <D.2840, {.m_size=0}>}
> 
> where the TARGET_EXPR is no longer direct-initializing anything.

Seems like the problem is in convert_like_internal:

>             bool direct = CONSTRUCTOR_IS_DIRECT_INIT (expr);
>             if (abstract_virtuals_error (NULL_TREE, totype, complain))
>               return error_mark_node;
>             expr = build_value_init (totype, complain);
>             expr = get_target_expr (expr, complain);
>             if (expr != error_mark_node)
>               {
>                 TARGET_EXPR_LIST_INIT_P (expr) = true;
> =>              TARGET_EXPR_DIRECT_INIT_P (expr) = direct;
>               }

My patch for 50930 assumed that if a CONSTRUCTOR represents syntactic 
direct-initialization, a resulting TARGET_EXPR is itself the direct 
initializer, but that isn't the case here; the temporary is 
copy-initialized.

We could calculate direct-initializanity from cand->flags, but perhaps 
we can just stop trying to set TARGET_EXPR_DIRECT_INIT_P here at all? 
We don't do that for other list-initialization in ck_user, I don't know 
why I thought it was needed for {} specifically.  It doesn't seem to be 
needed for the 50930 testcase.

Jason
  

Patch

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 5f16994300f..5707d96a9e7 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -7303,6 +7303,13 @@  cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
 	return t;
       }
 
+    case TARGET_EXPR:
+      /* We're going to be taking the address of this TARGET_EXPR, so
+	 it cannot be expected to be elided.  */
+      TARGET_EXPR_DIRECT_INIT_P (arg) = false;
+      TARGET_EXPR_ELIDING_P (arg) = false;
+      break;
+
     default:
       break;
     }
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr22.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr22.C
new file mode 100644
index 00000000000..a4f9ae19ca9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr22.C
@@ -0,0 +1,12 @@ 
+// PR c++/114854
+// { dg-do compile { target c++14 } }
+
+struct Vector {
+  int m_size;
+};
+struct S {
+  const Vector &vec{};
+};
+
+void spawn(S);
+void test() { spawn({}); }