On Wed, Jan 29, 2025 at 08:03:37AM -0500, Jason Merrill wrote:
> On 1/27/25 6:19 PM, Marek Polacek wrote:
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/14?
> >
> > -- >8 --
> > We've had a wrong-code problem since r14-4140, due to which we
> > forget to initialize a variable.
> >
> > In consteval39.C, we evaluate
> >
> > struct QQQ q;
> > <<cleanup_point <<< Unknown tree: expr_stmt
> > QQQ::QQQ (&q, TARGET_EXPR <D.2687, <<< Unknown tree: aggr_init_expr
> > 5
> > __ct_comp
> > D.2687
> > (struct basic_string_view *) <<< Unknown tree: void_cst >>>
> > (const char *) "" >>>>) >>>>>;
> >
> > into
> >
> > struct QQQ q;
> > <<cleanup_point <<< Unknown tree: expr_stmt
> > {.data={._M_len=42, ._M_str=0}} >>>>>;
> >
> > and then the useless expr_stmt is dropped on the floor, so q isn't
> > initialized. As pre-r14-4140, we need to handle constructors specially.
>
> Hmm, why didn't the following code in eval_outermost make this a
> rejects-valid bug rather than wrong-code?
>
> > /* If T is calling a constructor to initialize an object,
> > reframe
> > it as an AGGR_INIT_EXPR to avoid trying to modify an object
> > from outside the constant evaluation, which will fail even if
> > the value is actually constant (is_constant_evaluated3.C). */
So yes, we go here, create an AGGR_INIT_EXPR, then evaluate it into
{.data={._M_len=42, ._M_str=0}}. What should give an error?
> Your change should share code with this block doing the same thing in
> cp_fold_r:
>
> > if (TREE_CODE (r) != CALL_EXPR)
> > {
> > if (DECL_CONSTRUCTOR_P (callee))
> > {
> > loc = EXPR_LOCATION (x);
> > tree a = CALL_EXPR_ARG (x, 0);
> > bool return_this = targetm.cxx.cdtor_returns_this ();
> > if (return_this)
> > a = cp_save_expr (a);
> > tree s = build_fold_indirect_ref_loc (loc, a);
> > r = cp_build_init_expr (s, r);
> > if (return_this)
> > r = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (x), r,
> > fold_convert_loc (loc, TREE_TYPE (x), a));
> > }
> > x = r;
> > break;
> > }
>
> Like there, we shouldn't need this for AGGR_INIT_EXPR, only CALL_EXPR.
Okay, but that then means that I can't call cxx_constant_value with an
object, otherwise I don't think I can unify the code above. Which is
fine, we go down the eval_outermost/AGGR_INIT_EXPR path as mentioned
above. At least I hope that is okay.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/14?
-- >8 --
We've had a wrong-code problem since r14-4140, due to which we
forget to initialize a variable.
In consteval39.C, we evaluate
struct QQQ q;
<<cleanup_point <<< Unknown tree: expr_stmt
QQQ::QQQ (&q, TARGET_EXPR <D.2687, <<< Unknown tree: aggr_init_expr
5
__ct_comp
D.2687
(struct basic_string_view *) <<< Unknown tree: void_cst >>>
(const char *) "" >>>>) >>>>>;
into
struct QQQ q;
<<cleanup_point <<< Unknown tree: expr_stmt
{.data={._M_len=42, ._M_str=0}} >>>>>;
and then the useless expr_stmt is dropped on the floor, so q isn't
initialized. As pre-r14-4140, we need to handle constructors specially.
With this patch, we generate:
struct QQQ q;
<<cleanup_point <<< Unknown tree: expr_stmt
q = {.data={._M_len=42, ._M_str=0}} >>>>>;
initializing q properly.
PR c++/117501
gcc/cp/ChangeLog:
* cp-gimplify.cc (cp_build_init_expr_for_ctor): New.
(cp_fold_immediate_r): Call it.
(cp_fold): Break out code into cp_build_init_expr_for_ctor.
gcc/testsuite/ChangeLog:
* g++.dg/cpp2a/consteval39.C: New test.
* g++.dg/cpp2a/consteval40.C: New test.
---
gcc/cp/cp-gimplify.cc | 41 ++++++++++++++++--------
gcc/testsuite/g++.dg/cpp2a/consteval39.C | 27 ++++++++++++++++
gcc/testsuite/g++.dg/cpp2a/consteval40.C | 25 +++++++++++++++
3 files changed, 80 insertions(+), 13 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval39.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval40.C
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 4ec3de13008..bdbdcafec54 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -1182,6 +1182,27 @@ taking_address_of_imm_fn_error (tree expr, tree decl)
maybe_explain_promoted_consteval (loc, decl);
}
+/* Build up an INIT_EXPR to initialize the object of a constructor. CALL
+ is the CALL_EXPR for the constructor call; INIT is the initializer. */
+
+static tree
+cp_build_init_expr_for_ctor (tree call, tree init)
+{
+ tree a = CALL_EXPR_ARG (call, 0);
+ if (is_dummy_object (a))
+ return init;
+ const bool return_this = targetm.cxx.cdtor_returns_this ();
+ const location_t loc = EXPR_LOCATION (call);
+ if (return_this)
+ a = cp_save_expr (a);
+ tree s = build_fold_indirect_ref_loc (loc, a);
+ init = cp_build_init_expr (s, init);
+ if (return_this)
+ init = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (call), init,
+ fold_convert_loc (loc, TREE_TYPE (call), a));
+ return init;
+}
+
/* A subroutine of cp_fold_r to handle immediate functions. */
static tree
@@ -1297,7 +1318,12 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees,
void *data_)
}
/* We've evaluated the consteval function call. */
if (call_p)
- *stmt_p = e;
+ {
+ if (code == CALL_EXPR && DECL_CONSTRUCTOR_P (decl))
+ *stmt_p = cp_build_init_expr_for_ctor (stmt, e);
+ else
+ *stmt_p = e;
+ }
}
/* We've encountered a function call that may turn out to be consteval
later. Store its caller so that we can ensure that the call is
@@ -3422,18 +3448,7 @@ cp_fold (tree x, fold_flags_t flags)
if (TREE_CODE (r) != CALL_EXPR)
{
if (DECL_CONSTRUCTOR_P (callee))
- {
- loc = EXPR_LOCATION (x);
- tree a = CALL_EXPR_ARG (x, 0);
- bool return_this = targetm.cxx.cdtor_returns_this ();
- if (return_this)
- a = cp_save_expr (a);
- tree s = build_fold_indirect_ref_loc (loc, a);
- r = cp_build_init_expr (s, r);
- if (return_this)
- r = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (x), r,
- fold_convert_loc (loc, TREE_TYPE (x), a));
- }
+ r = cp_build_init_expr_for_ctor (x, r);
x = r;
break;
}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval39.C
b/gcc/testsuite/g++.dg/cpp2a/consteval39.C
new file mode 100644
index 00000000000..523e8260eab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval39.C
@@ -0,0 +1,27 @@
+// PR c++/117501
+// { dg-do run { target c++20 } }
+
+constexpr unsigned
+length ()
+{
+ bool __trans_tmp_1 = __builtin_is_constant_evaluated();
+ if (__trans_tmp_1)
+ return 42;
+ return 1;
+}
+struct basic_string_view {
+ constexpr basic_string_view(const char *) : _M_len{length()}, _M_str{} {}
+ long _M_len;
+ char _M_str;
+};
+struct QQQ {
+ consteval QQQ(basic_string_view d) : data(d) {}
+ basic_string_view data;
+};
+int
+main ()
+{
+ QQQ q("");
+ if (q.data._M_len != 42)
+ __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval40.C
b/gcc/testsuite/g++.dg/cpp2a/consteval40.C
new file mode 100644
index 00000000000..4d3ba20092b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval40.C
@@ -0,0 +1,25 @@
+// PR c++/117501
+// { dg-do run { target c++20 } }
+
+constexpr int
+twiddle (int i)
+{
+ if (__builtin_is_constant_evaluated ())
+ return 3;
+ return i;
+}
+struct S {
+ constexpr S(int i) : i{twiddle (i)} {}
+ int i;
+};
+struct Q {
+ consteval Q(S s_) : s{s_, s_} {}
+ S s[2];
+};
+int
+main ()
+{
+ Q q(twiddle (42));
+ if (q.s[0].i != 3 || q.s[1].i != 3)
+ __builtin_abort ();
+}
base-commit: 1e819a997dd5507e52cafc540656fc15160322fd
--
2.48.1