public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r14-10134] c++: Fix constexpr evaluation of parameters passed by invisible reference [PR111284]
@ 2024-04-25 18:46 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2024-04-25 18:46 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:f541757ba4632e204169dd08a5f10c782199af42

commit r14-10134-gf541757ba4632e204169dd08a5f10c782199af42
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Apr 25 20:45:04 2024 +0200

    c++: Fix constexpr evaluation of parameters passed by invisible reference [PR111284]
    
    My r9-6136 changes to make a copy of constexpr function bodies before
    genericization modifies it broke the constant evaluation of non-POD
    arguments passed by value.
    In the callers such arguments are passed as reference to usually a
    TARGET_EXPR, but on the callee side until genericization they are just
    direct uses of a PARM_DECL with some class type.
    In cxx_bind_parameters_in_call I've used convert_from_reference to
    pretend it is passed by value and then cxx_eval_constant_expression
    is called there and evaluates that as an rvalue, followed by
    adjust_temp_type if the types don't match exactly (e.g. const Foo
    argument and passing to it reference to Foo TARGET_EXPR).
    
    The reason this doesn't work is that when the TARGET_EXPR in the caller
    is constant initialized, this for it is the address of the TARGET_EXPR_SLOT,
    but if the code later on pretends the PARM_DECL is just initialized to the
    rvalue of the constant evaluation of the TARGET_EXPR, it is as if there
    is a bitwise copy of the TARGET_EXPR to the callee, so this in the callee
    is then address of the PARM_DECL in the callee.
    
    The following patch attempts to fix that by constexpr evaluation of such
    arguments in the caller as an lvalue instead of rvalue, and on the callee
    side when seeing such a PARM_DECL, if we want an lvalue, lookup the value
    (lvalue) saved in ctx->globals (if any), and if wanting an rvalue,
    recursing with vc_prvalue on the looked up value (because it is there
    as an lvalue, nor rvalue).
    
    adjust_temp_type doesn't work for lvalues of non-scalarish types, for
    such types it relies on changing the type of a CONSTRUCTOR, but on the
    other side we know what we pass to the argument is addressable, so
    the patch on type mismatch takes address of the argument value, casts
    to reference to the desired type and dereferences it.
    
    2024-04-25  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/111284
            * constexpr.cc (cxx_bind_parameters_in_call): For PARM_DECLs with
            TREE_ADDRESSABLE types use vc_glvalue rather than vc_prvalue for
            cxx_eval_constant_expression and if it doesn't have the same
            type as it should, cast the reference type to reference to type
            before convert_from_reference and instead of adjust_temp_type
            take address of the arg, cast to reference to type and then
            convert_from_reference.
            (cxx_eval_constant_expression) <case PARM_DECL>: For lval case
            on parameters with TREE_ADDRESSABLE types lookup result in
            ctx->globals if possible.  Otherwise if lookup in ctx->globals
            was successful for parameter with TREE_ADDRESSABLE type,
            recurse with vc_prvalue on the returned value.
    
            * g++.dg/cpp1z/constexpr-111284.C: New test.
            * g++.dg/cpp1y/constexpr-lifetime7.C: Expect one error on a different
            line.

Diff:
---
 gcc/cp/constexpr.cc                              | 44 ++++++++++++++++++------
 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime7.C |  2 +-
 gcc/testsuite/g++.dg/cpp1z/constexpr-111284.C    | 19 ++++++++++
 3 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 2e83d24dfda..8078b31544d 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -1872,13 +1872,18 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
 	  x = build_address (x);
 	}
       if (TREE_ADDRESSABLE (type))
-	/* Undo convert_for_arg_passing work here.  */
-	x = convert_from_reference (x);
-      /* Normally we would strip a TARGET_EXPR in an initialization context
-	 such as this, but here we do the elision differently: we keep the
-	 TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm.  */
-      arg = cxx_eval_constant_expression (ctx, x, vc_prvalue,
-					  non_constant_p, overflow_p);
+	{
+	  /* Undo convert_for_arg_passing work here.  */
+	  x = convert_from_reference (x);
+	  arg = cxx_eval_constant_expression (ctx, x, vc_glvalue,
+					      non_constant_p, overflow_p);
+	}
+      else
+	/* Normally we would strip a TARGET_EXPR in an initialization context
+	   such as this, but here we do the elision differently: we keep the
+	   TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm.  */
+	arg = cxx_eval_constant_expression (ctx, x, vc_prvalue,
+					    non_constant_p, overflow_p);
       /* Check we aren't dereferencing a null pointer when calling a non-static
 	 member function, which is undefined behaviour.  */
       if (i == 0 && DECL_OBJECT_MEMBER_FUNCTION_P (fun)
@@ -1904,7 +1909,16 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
 	{
 	  /* Make sure the binding has the same type as the parm.  But
 	     only for constant args.  */
-	  if (!TYPE_REF_P (type))
+	  if (TREE_ADDRESSABLE (type))
+	    {
+	      if (!same_type_p (type, TREE_TYPE (arg)))
+		{
+		  arg = build_fold_addr_expr (arg);
+		  arg = cp_fold_convert (build_reference_type (type), arg);
+		  arg = convert_from_reference (arg);
+		}
+	    }
+	  else if (!TYPE_REF_P (type))
 	    arg = adjust_temp_type (type, arg);
 	  if (!TREE_CONSTANT (arg))
 	    *non_constant_args = true;
@@ -7499,9 +7513,19 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 
     case PARM_DECL:
       if (lval && !TYPE_REF_P (TREE_TYPE (t)))
-	/* glvalue use.  */;
+	{
+	  /* glvalue use.  */
+	  if (TREE_ADDRESSABLE (TREE_TYPE (t)))
+	    if (tree v = ctx->global->get_value (t))
+	      r = v;
+	}
       else if (tree v = ctx->global->get_value (t))
-	r = v;
+	{
+	  r = v;
+	  if (TREE_ADDRESSABLE (TREE_TYPE (t)))
+	    r = cxx_eval_constant_expression (ctx, r, vc_prvalue,
+					      non_constant_p, overflow_p);
+	}
       else if (lval)
 	/* Defer in case this is only used for its type.  */;
       else if (ctx->global->is_outside_lifetime (t))
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime7.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime7.C
index 4148f42f7be..a1da4f81201 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime7.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime7.C
@@ -87,7 +87,7 @@ constexpr bool n1 = test_access<NonTrivial>();        // { dg-message "in .const
 constexpr bool n2 = test_modification<NonTrivial>();  // { dg-message "in .constexpr." "" { target c++20 } }
 constexpr bool n3 = test_scope<NonTrivial>();         // { dg-message "in .constexpr." "" { target c++20 } }
 constexpr bool n4 = test_destroy_temp<NonTrivial>();  // { dg-message "in .constexpr." "" { target c++20 } }
-constexpr bool n5 = test_parameter(NonTrivial{});     // { dg-error "destroying" "" { target c++20 } }
+constexpr bool n5 = test_parameter(NonTrivial{});     // { dg-message "in .constexpr." "" { target c++20 } }
 constexpr bool n6 = test_bindings<NonTrivial>();
 #endif
 
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-111284.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-111284.C
new file mode 100644
index 00000000000..eb31fd843e6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-111284.C
@@ -0,0 +1,19 @@
+// PR c++/111284
+// { dg-do compile { target c++17 } }
+
+struct S {
+  S () = default;
+  constexpr S (const S &) noexcept : s{this} {}
+  constexpr S & operator= (const S &) noexcept { return *this; }
+  constexpr bool foo () const noexcept { return s == this; }
+  S *s = this;
+};
+
+constexpr bool
+bar (S x) noexcept
+{
+  return x.foo ();
+}
+
+static_assert (bar (S {}), "");
+static_assert ([] (S x) { return x.foo (); } (S {}), "");

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-04-25 18:46 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-25 18:46 [gcc r14-10134] c++: Fix constexpr evaluation of parameters passed by invisible reference [PR111284] Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).