On Tue, Dec 17, 2024 at 11:43:45AM -0500, Jason Merrill wrote:
> On 12/12/24 1:42 PM, Marek Polacek wrote:
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> >
> > -- >8 --
> > This ICE started with the recent prvalue optimization (r15-6052). In
> > cp_fold_r we have:
> >
> > if (tree &init = TARGET_EXPR_INITIAL (stmt))
> > {
> > cp_walk_tree (&init, cp_fold_r, data, NULL);
> > // ...
> > tree folded = maybe_constant_init (init, TARGET_EXPR_SLOT (stmt));
> >
> > What can happen here is that originally the TARGET_EXPR is:
> >
> > TARGET_EXPR <D.2747, <<< Unknown tree: aggr_init_expr
> > 5
> > __ct_comp
> > D.2747
> > (struct subrange *) <<< Unknown tree: void_cst >>>
> > &TARGET_EXPR <D.2707, {.it=TARGET_EXPR <D.2695, ...>}> >>>>
> >
> > but after the first cp_walk_tree we fold the D.2707 TARGET_EXPR into:
> >
> > TARGET_EXPR <D.2707, <<< Unknown tree: expr_stmt
> > D.2707.it = TARGET_EXPR <D.2695, ...> >>>>
> >
> > and then we pass the EXPR_STMT to maybe_constant_init, with D.2707 as
> > the object. But their types don't match anymore, so we crash. We'd
> > have to pass D.2707.it as the object for it to work.
> >
> > But I don't think we need to pass any object to maybe_constant_init;
> > it'll grab the appropriate one itself.
>
> Hmm, it seems to me that the crash is happening because the deduced type is
> wrong, and so with this change we'll end up only producing an initializer
> for the first member. Because cp_fold_r throws away the information that
> initialized_type needs to know the type of the complete object being
> initialized.
Thanks for catching that.
> What if we move cp_fold_r of init after the maybe_constant_init?
Sadly, the same crash.
But your idea works, so this is it in a patch form. Thanks again.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
This ICE started with the recent prvalue optimization (r15-6052). In
cp_fold_r we have:
if (tree &init = TARGET_EXPR_INITIAL (stmt))
{
cp_walk_tree (&init, cp_fold_r, data, NULL);
// ...
tree folded = maybe_constant_init (init, TARGET_EXPR_SLOT (stmt));
What can happen here is that originally the TARGET_EXPR is:
TARGET_EXPR <D.2747, <<< Unknown tree: aggr_init_expr
5
__ct_comp
D.2747
(struct subrange *) <<< Unknown tree: void_cst >>>
&TARGET_EXPR <D.2707, {.it=TARGET_EXPR <D.2695, ...>}> >>>>
but after the first cp_walk_tree we fold the D.2707 TARGET_EXPR into:
TARGET_EXPR <D.2707, <<< Unknown tree: expr_stmt
D.2707.it = TARGET_EXPR <D.2695, ...> >>>>
and then we pass the EXPR_STMT to maybe_constant_init, with D.2707 as
the object. But their types don't match anymore, so we crash. We'd
have to pass D.2707.it as the object for it to work.
This patch adjusts cxx_eval_outermost_constant_expr to take the object's
type if available.
constexpr-prvalue3.C is reduced from a large std::ranges libstdc++ test.
PR c++/117980
gcc/cp/ChangeLog:
* constexpr.cc (cxx_eval_outermost_constant_expr): If there's
an object to initialize, take its type.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/constexpr-prvalue2.C: New test.
* g++.dg/cpp0x/constexpr-prvalue3.C: New test.
Co-authored-by: Jason Merrill <[email protected]>
---
gcc/cp/constexpr.cc | 4 ++-
.../g++.dg/cpp0x/constexpr-prvalue2.C | 15 +++++++++++
.../g++.dg/cpp0x/constexpr-prvalue3.C | 26 +++++++++++++++++++
3 files changed, 44 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue2.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue3.C
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index c16597dfaec..f941a1eabd8 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -8870,7 +8870,9 @@ cxx_eval_outermost_constant_expr (tree t, bool
allow_non_constant,
/* Turn off -frounding-math for manifestly constant evaluation. */
warning_sentinel rm (flag_rounding_math,
ctx.manifestly_const_eval == mce_true);
- tree type = initialized_type (t);
+ tree type = (object
+ ? cv_unqualified (TREE_TYPE (object))
+ : initialized_type (t));
tree r = t;
bool is_consteval = false;
if (VOID_TYPE_P (type))
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue2.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue2.C
new file mode 100644
index 00000000000..46053231cf8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue2.C
@@ -0,0 +1,15 @@
+// PR c++/117980
+// { dg-do compile { target c++11 } }
+// { dg-options "-O" }
+
+struct S {
+ constexpr S(S &); // { dg-warning "used but never defined" }
+ ~S();
+};
+struct B {
+ S s;
+};
+struct A {
+ B b;
+};
+void fn(B b) { A{b}; }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue3.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue3.C
new file mode 100644
index 00000000000..a2eb12c02d7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue3.C
@@ -0,0 +1,26 @@
+// PR c++/117980
+// { dg-do compile { target c++11 } }
+// { dg-options "-O" }
+
+struct _Safe_iterator {
+ _Safe_iterator();
+ ~_Safe_iterator();
+};
+template <typename _Tp>
+struct vector {
+ vector(int) {}
+ constexpr _Safe_iterator end() {
+ return _Safe_iterator();
+ }
+};
+template <typename It> struct sentinel {
+ It it;
+};
+template <typename _Sent>
+struct subrange {
+ subrange(sentinel<_Safe_iterator>) {}
+};
+void test01() {
+ vector<int> v{0};
+ subrange<sentinel<_Safe_iterator>>{sentinel<_Safe_iterator>{v.end()}};
+}
base-commit: 17d8a3da25cd5262df1075b210cb4fa707c527df
--
2.47.1