On Fri, 5 Mar 2021, Jason Merrill wrote:

> On 3/5/21 1:05 PM, Patrick Palka wrote:
> > Here, during cxx_eval_increment_expression (with lval=false) of
> > ++__first where __first is &"mystr"[0], we correctly update __first
> > to &"mystr"[1] but we end up returning &"mystr"[0] + 1 instead of
> > &"mystr"[1].  This unreduced return value inhibits other pointer
> > arithmetic folding during later constexpr evaluation, which ultimately
> > causes the constexpr evaluation to fail.
> > 
> > It turns out the simplification of &"mystr"[0] + 1 to &"mystr"[1]
> > is performed by cxx_fold_pointer_plus_expression, not by fold_build2.
> > So we perform this simplification during constexpr evaluation of
> > the temporary MODIFY_EXPR (assigning to __first the simplified value),
> > but then we return 'mod' which has only been folded via fold_build2 and
> > hasn't gone through cxx_fold_pointer_plus_expression.
> > 
> > This patch fixes this by updating 'mod' to the (rvalue) result of the
> > MODIFY_EXPR evaluation, so that we capture any additional folding of
> > 'mod'.  We now need to be wary of the evaluation failing and returning
> > e.g. the MODIFY_EXPR or NULL_TREE; it seems checking *non_constant_p
> > should cover our bases here and is generally prudent.
> > 
> > (Finally, since returning 'mod' instead of 'op' when !lval seems to be
> > more than just an optimization, i.e. callers seems to expect this
> > behavior, this patch additionally clarifies the nearby comment to that
> > effect.)
> > 
> > Boostrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> > trunk or perhaps GCC 12?
> > 
> > gcc/cp/ChangeLog:
> > 
> >     PR c++/99287
> >     * constexpr.c (cxx_eval_increment_expression): Pass lval=false
> >     when evaluating the MODIFY_EXPR, and update 'mod' with the
> >     result of this evaluation.  Check *non_constant_p afterwards.
> >     Clarify nearby comment.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     PR c++/99287
> >     * g++.dg/cpp2a/constexpr-99287.C: New test.
> > 
> > Co-authored-by: Jakub Jelinek <ja...@redhat.com>
> > ---
> >   gcc/cp/constexpr.c                           | 16 ++---
> >   gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C | 61 ++++++++++++++++++++
> >   2 files changed, 67 insertions(+), 10 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C
> > 
> > diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> > index cd0a68e9fd6..49df79837ca 100644
> > --- a/gcc/cp/constexpr.c
> > +++ b/gcc/cp/constexpr.c
> > @@ -5582,20 +5582,16 @@ cxx_eval_increment_expression (const constexpr_ctx
> > *ctx, tree t,
> >     /* Storing the modified value.  */
> >     tree store = build2_loc (cp_expr_loc_or_loc (t, input_location),
> >                        MODIFY_EXPR, type, op, mod);
> > -  cxx_eval_constant_expression (ctx, store,
> > -                           true, non_constant_p, overflow_p);
> > +  mod = cxx_eval_constant_expression (ctx, store, false,
> 
> How about passing lval down here and returning mod either way?

Sounds good, like this?  Testing in progress

-- >8 --

Subject: [PATCH] c++: Fix constexpr evaluation of pre-increment when !lval
 [PR99287]

Here, during cxx_eval_increment_expression (with lval=false) of
++__first where __first is &"mystr"[0], we correctly update __first
to &"mystr"[1] but we end up returning &"mystr"[0] + 1 instead of
&"mystr"[1].  This unreduced return value inhibits other pointer
arithmetic folding during later constexpr evaluation, which ultimately
causes the constexpr evaluation to fail.

It turns out the simplification of &"mystr"[0] + 1 to &"mystr"[1]
is performed by cxx_fold_pointer_plus_expression, not by fold_build2.
So we perform this simplification during constexpr evaluation of
the temporary MODIFY_EXPR (during which we assign to __first the
simplified value), but then we return 'mod' which has only been folded
via fold_build2 and hasn't gone through cxx_fold_pointer_plus_expression.

This patch fixes this by also updating 'mod' with the result of the
MODIFY_EXPR evaluation appropriately, so that we capture any additional
folding of the expression when !lval.  We now need to be wary of this
evaluation failing and returning e.g. the MODIFY_EXPR or NULL_TREE; it
seems checking *non_constant_p should cover our bases here and is
generally prudent.

gcc/cp/ChangeLog:

        PR c++/99287
        * constexpr.c (cxx_eval_increment_expression): Pass lval when
        evaluating the MODIFY_EXPR, and update 'mod' with the result of
        this evaluation.  Check *non_constant_p afterwards.  For prefix
        ops, just return 'mod'.

gcc/testsuite/ChangeLog:

        PR c++/99287
        * g++.dg/cpp2a/constexpr-99287.C: New test.

Co-authored-by: Jakub Jelinek <ja...@redhat.com>
---
 gcc/cp/constexpr.c                           | 17 +++---
 gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C | 61 ++++++++++++++++++++
 2 files changed, 68 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 7d96d577d84..d7150b25b19 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -5582,20 +5582,17 @@ cxx_eval_increment_expression (const constexpr_ctx 
*ctx, tree t,
   /* Storing the modified value.  */
   tree store = build2_loc (cp_expr_loc_or_loc (t, input_location),
                           MODIFY_EXPR, type, op, mod);
-  cxx_eval_constant_expression (ctx, store,
-                               true, non_constant_p, overflow_p);
+  mod = cxx_eval_constant_expression (ctx, store, lval,
+                                     non_constant_p, overflow_p);
   ggc_free (store);
+  if (*non_constant_p)
+    return t;
 
   /* And the value of the expression.  */
   if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
-    {
-      /* Prefix ops are lvalues.  */
-      if (lval)
-       return op;
-      else
-       /* But we optimize when the caller wants an rvalue.  */
-       return mod;
-    }
+    /* Prefix ops are lvalues, but the caller might want an rvalue.
+       This has already been taken into account by the store above.  */
+    return mod;
   else
     /* Postfix ops are rvalues.  */
     return val;
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C 
b/gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C
new file mode 100644
index 00000000000..c9c2ac2f7c2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C
@@ -0,0 +1,61 @@
+// PR c++/99287
+// { dg-do compile { target c++20 } }
+
+namespace std {
+struct source_location {
+  static consteval source_location
+  current(const void *__p = __builtin_source_location()) {
+    source_location __ret;
+    __ret._M_impl = static_cast<const __impl *>(__p);
+    return __ret;
+  }
+  constexpr const char *function_name() {
+    return _M_impl ? _M_impl->_M_function_name : "";
+  }
+  struct __impl {
+    const char *_M_file_name;
+    const char *_M_function_name;
+    unsigned _M_line;
+    unsigned _M_column;
+  } const *_M_impl;
+};
+struct char_traits {
+  static constexpr long length(const char *__s) {
+    return __builtin_strlen(__s);
+  }
+};
+template <typename _CharT, typename _Traits = char_traits>
+class basic_string_view {
+public:
+  using traits_type = _Traits;
+  using size_type = unsigned long;
+  constexpr basic_string_view(const _CharT *__str)
+      : _M_len{traits_type::length(__str)}, _M_str{__str} {}
+  constexpr size_type find(const _CharT *, size_type, size_type) const 
noexcept;
+  constexpr size_type find(_CharT *__str) {
+    long __trans_tmp_1 = traits_type::length(__str);
+    return find(__str, 0, __trans_tmp_1);
+  }
+  long _M_len;
+  const _CharT *_M_str;
+};
+using string_view = basic_string_view<const char>;
+template <typename _CharT, typename _Traits>
+constexpr unsigned long
+basic_string_view<_CharT, _Traits>::find(const _CharT *__str, size_type,
+                                         size_type __n) const noexcept {
+  int __trans_tmp_2;
+  const _CharT *__first = _M_str;
+  size_type __len = _M_len;
+  while (__len >= __n) {
+    __trans_tmp_2 = __builtin_memcmp(__first, __str, __n);
+    if (__trans_tmp_2 == 0)
+      return __first - _M_str;
+    __len = _M_str - ++__first;
+  }
+}
+} // namespace std
+template <typename> consteval auto f() {
+  return std::string_view{std::source_location::current().function_name()};
+}
+int main() { constexpr auto s = f<int>().find("int"); }
-- 
2.31.0.rc0.75.gec125d1bc1

Reply via email to