Re: [C++ PATCH] Fix cxx_eval_loop_expr ICE (PR c++/89652, take 2)

2019-03-13 Thread Jason Merrill

On 3/13/19 6:20 PM, Jakub Jelinek wrote:

On Mon, Mar 11, 2019 at 11:21:00PM +0100, Jakub Jelinek wrote:

The following testcase ICEs since my recent cxx_eval_loop_expr changes.
The problem is that the Forget saved values of SAVE_EXPRs. inside of the
loop can remove SAVE_EXPRs from new_ctx.values and if that is the last
iteration, we can also do the loop at the end of function (which has been
added there mainly to handle cases where the main loop breaks earlier)
and new_ctx.values->remove ICEs because *iter is already not in the table.

While I could e.g. add some bool whether there is anything to be removed
after the loop or not which would fix the regression part of this PR,
I believe there is a latent issue as well.  The thing is, new_ctx.save_exprs


That is actually not true, the bug is fully my fault and previously it was
ok, as cxx_eval_loop_expr used to do:
   do
 {
   hash_set save_exprs;
   new_ctx.save_exprs = _exprs;
...
 }
   while (...)
and thus there was a new hash_set for each iteration, it was just me moving
it out of the loop (so that I don't have to repeat the ->remove loop on each
break or use gotos).


Yet another possibility might be to turn save_exprs into a vec, from what


That said, I believe using a vec must be faster and the hash_set seems
overkill, as if SAVE_EXPR wasn't seen yet in the current iteration, it is
desirable to add it to the vec and if it has been added there,
ctx->value->get will be non-NULL, so we'll never add a duplicate, and by
using a vec we don't have to allocate storage for each iteration etc.

So, here is a variant patch I've bootstrapped/regtested on x86_64-linux and
i686-linux, ok for trunk?


This is OK.  Is my hash-table.h fix also OK for trunk?  It does also fix 
the testcase.


Jason


Re: [C++ PATCH] Fix cxx_eval_loop_expr ICE (PR c++/89652)

2019-03-13 Thread Jason Merrill

On 3/13/19 10:08 PM, Jason Merrill wrote:

On 3/11/19 6:21 PM, Jakub Jelinek wrote:

Hi!

The following testcase ICEs since my recent cxx_eval_loop_expr changes.
The problem is that the Forget saved values of SAVE_EXPRs. inside of the
loop can remove SAVE_EXPRs from new_ctx.values and if that is the last
iteration, we can also do the loop at the end of function (which has been
added there mainly to handle cases where the main loop breaks earlier)
and new_ctx.values->remove ICEs because *iter is already not in the 
table.


It shouldn't ICE, remove_elt_with_hash is documented to do nothing if 
the element is not in the table.


Ah, now I see you sent a follow-up, reading that now.

Jason



Re: [C++ PATCH] Fix cxx_eval_loop_expr ICE (PR c++/89652)

2019-03-13 Thread Jason Merrill

On 3/11/19 6:21 PM, Jakub Jelinek wrote:

Hi!

The following testcase ICEs since my recent cxx_eval_loop_expr changes.
The problem is that the Forget saved values of SAVE_EXPRs. inside of the
loop can remove SAVE_EXPRs from new_ctx.values and if that is the last
iteration, we can also do the loop at the end of function (which has been
added there mainly to handle cases where the main loop breaks earlier)
and new_ctx.values->remove ICEs because *iter is already not in the table.


It shouldn't ICE, remove_elt_with_hash is documented to do nothing if 
the element is not in the table.


Does this untested patch fix the test?

Jason
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 1fd36946a53..fb2f77fdbee 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -940,7 +940,7 @@ hash_table
 ::remove_elt_with_hash (const compare_type , hashval_t hash)
 {
   value_type *slot = find_slot_with_hash (comparable, hash, NO_INSERT);
-  if (is_empty (*slot))
+  if (!slot || is_empty (*slot))
 return;
 
   Descriptor::remove (*slot);


[C++ PATCH] Fix cxx_eval_loop_expr ICE (PR c++/89652, take 2)

2019-03-13 Thread Jakub Jelinek
On Mon, Mar 11, 2019 at 11:21:00PM +0100, Jakub Jelinek wrote:
> The following testcase ICEs since my recent cxx_eval_loop_expr changes.
> The problem is that the Forget saved values of SAVE_EXPRs. inside of the
> loop can remove SAVE_EXPRs from new_ctx.values and if that is the last
> iteration, we can also do the loop at the end of function (which has been
> added there mainly to handle cases where the main loop breaks earlier)
> and new_ctx.values->remove ICEs because *iter is already not in the table.
> 
> While I could e.g. add some bool whether there is anything to be removed
> after the loop or not which would fix the regression part of this PR,
> I believe there is a latent issue as well.  The thing is, new_ctx.save_exprs

That is actually not true, the bug is fully my fault and previously it was
ok, as cxx_eval_loop_expr used to do:
  do
{
  hash_set save_exprs;
  new_ctx.save_exprs = _exprs;
...
}
  while (...)
and thus there was a new hash_set for each iteration, it was just me moving
it out of the loop (so that I don't have to repeat the ->remove loop on each
break or use gotos).

> Yet another possibility might be to turn save_exprs into a vec, from what

That said, I believe using a vec must be faster and the hash_set seems
overkill, as if SAVE_EXPR wasn't seen yet in the current iteration, it is
desirable to add it to the vec and if it has been added there,
ctx->value->get will be non-NULL, so we'll never add a duplicate, and by
using a vec we don't have to allocate storage for each iteration etc.

So, here is a variant patch I've bootstrapped/regtested on x86_64-linux and
i686-linux, ok for trunk?

2019-03-13  Jakub Jelinek  

PR c++/89652
* constexpr.c (struct constexpr_ctx): Change save_exprs type from
hash_set to vec.
(cxx_eval_call_expression): Adjust for save_exprs being a vec instead
of hash_set.
(cxx_eval_loop_expr): Likewise.  Truncate the vector after each
removal of SAVE_EXPRs from values.
(cxx_eval_constant_expression) : Call safe_push
method on save_exprs instead of add.

* g++.dg/cpp1y/constexpr-89652.C: New test.

--- gcc/cp/constexpr.c.jj   2019-03-11 22:56:51.856732730 +0100
+++ gcc/cp/constexpr.c  2019-03-13 13:21:48.986687463 +0100
@@ -1024,7 +1024,7 @@ struct constexpr_ctx {
   hash_map *values;
   /* SAVE_EXPRs that we've seen within the current LOOP_EXPR.  NULL if we
  aren't inside a loop.  */
-  hash_set *save_exprs;
+  vec *save_exprs;
   /* The CONSTRUCTOR we're currently building up for an aggregate
  initializer.  */
   tree ctor;
@@ -1831,7 +1831,7 @@ cxx_eval_call_expression (const constexp
  /* Track the callee's evaluated SAVE_EXPRs so that we can forget
 their values after the call.  */
  constexpr_ctx ctx_with_save_exprs = *ctx;
- hash_set save_exprs;
+ auto_vec save_exprs;
  ctx_with_save_exprs.save_exprs = _exprs;
  ctx_with_save_exprs.call = _call;
 
@@ -1862,9 +1862,10 @@ cxx_eval_call_expression (const constexp
}
 
  /* Forget the saved values of the callee's SAVE_EXPRs.  */
- for (hash_set::iterator iter = save_exprs.begin();
-  iter != save_exprs.end(); ++iter)
-   ctx_with_save_exprs.values->remove (*iter);
+ unsigned int i;
+ tree save_expr;
+ FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
+   ctx_with_save_exprs.values->remove (save_expr);
 
  /* Remove the parms/result from the values map.  Is it worth
 bothering to do this when the map itself is only live for
@@ -4190,7 +4191,7 @@ cxx_eval_loop_expr (const constexpr_ctx
 default:
   gcc_unreachable ();
 }
-  hash_set save_exprs;
+  auto_vec save_exprs;
   new_ctx.save_exprs = _exprs;
   do
 {
@@ -4234,9 +4235,11 @@ cxx_eval_loop_expr (const constexpr_ctx
}
 
   /* Forget saved values of SAVE_EXPRs.  */
-  for (hash_set::iterator iter = save_exprs.begin();
-  iter != save_exprs.end(); ++iter)
-   new_ctx.values->remove (*iter);
+  unsigned int i;
+  tree save_expr;
+  FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
+   new_ctx.values->remove (save_expr);
+  save_exprs.truncate (0);
 
   if (++count >= constexpr_loop_limit)
{
@@ -4256,9 +4259,10 @@ cxx_eval_loop_expr (const constexpr_ctx
 && !*non_constant_p);
 
   /* Forget saved values of SAVE_EXPRs.  */
-  for (hash_set::iterator iter = save_exprs.begin();
-   iter != save_exprs.end(); ++iter)
-new_ctx.values->remove (*iter);
+  unsigned int i;
+  tree save_expr;
+  FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
+new_ctx.values->remove (save_expr);
 
   return NULL_TREE;
 }
@@ -4616,7 +4620,7 @@ cxx_eval_constant_expression (const cons
non_constant_p, overflow_p);
  ctx->values->put (t, r);
  if (ctx->save_exprs)
-   

[C++ PATCH] Fix cxx_eval_loop_expr ICE (PR c++/89652)

2019-03-11 Thread Jakub Jelinek
Hi!

The following testcase ICEs since my recent cxx_eval_loop_expr changes.
The problem is that the Forget saved values of SAVE_EXPRs. inside of the
loop can remove SAVE_EXPRs from new_ctx.values and if that is the last
iteration, we can also do the loop at the end of function (which has been
added there mainly to handle cases where the main loop breaks earlier)
and new_ctx.values->remove ICEs because *iter is already not in the table.

While I could e.g. add some bool whether there is anything to be removed
after the loop or not which would fix the regression part of this PR,
I believe there is a latent issue as well.  The thing is, new_ctx.save_exprs
is a hash_set, which the cxx_eval_* calls add SAVE_EXPRs to whenever they
encounter new ones, but if we don't encounter all the previously seen
SAVE_EXPRs in every iteration (e.g. some are wrapped by IF_STMT or
similar and the condition differs between iterations), I believe we can
still ICE, trying to remove a SAVE_EXPR that has been encountered by some
earlier iteration, but not the current one.

The following patch is one possible fix, only remove from new_ctx.values
what is still in there.  Bootstrapped/regtested on x86_64-linux and
i686-linux, ok for trunk?  Or some variant mentioned below?

Another possibility would be to save_exprs.empty (); after this
/* Forget saved values of SAVE_EXPRs.  */ loop inside of main loop (no need
to do it at the end of function when we'll just destroy it anyway), the
advantage would be that we don't really walk over SAVE_EXPRs that weren't
saved in the current iteration, disadvantage is that we need to populate the
hash_set again and again each iteration.

Yet another possibility might be to turn save_exprs into a vec, from what
I understand, we do:
case SAVE_EXPR:
  /* Avoid evaluating a SAVE_EXPR more than once.  */
  if (tree *p = ctx->values->get (t))
r = *p;
  else
{
  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
non_constant_p, overflow_p);
  ctx->values->put (t, r);
  if (ctx->save_exprs)
ctx->save_exprs->add (t);
}
and thus shouldn't be adding duplicates to save_expr and if we flush it
every iteration, we could just if (ctx->save_exprs) ctx->save_exprs->safe_push 
(t);
and truncate the vector after every /* Forget saved values of SAVE_EXPRs.  */
loop in the main loop's body.  Thoughts on this?

2019-03-11  Jakub Jelinek  

PR c++/89652
* constexpr.c (cxx_eval_loop_expr): Only remove SAVE_EXPRs that are
still in new_ctx.values hash_map.

* g++.dg/cpp1y/constexpr-89652.C: New test.

--- gcc/cp/constexpr.c.jj   2019-03-08 08:43:23.529496048 +0100
+++ gcc/cp/constexpr.c  2019-03-11 15:11:32.081334270 +0100
@@ -4236,7 +4236,8 @@ cxx_eval_loop_expr (const constexpr_ctx
   /* Forget saved values of SAVE_EXPRs.  */
   for (hash_set::iterator iter = save_exprs.begin();
   iter != save_exprs.end(); ++iter)
-   new_ctx.values->remove (*iter);
+   if (new_ctx.values->get (*iter))
+ new_ctx.values->remove (*iter);
 
   if (++count >= constexpr_loop_limit)
{
@@ -4258,7 +4259,8 @@ cxx_eval_loop_expr (const constexpr_ctx
   /* Forget saved values of SAVE_EXPRs.  */
   for (hash_set::iterator iter = save_exprs.begin();
iter != save_exprs.end(); ++iter)
-new_ctx.values->remove (*iter);
+if (new_ctx.values->get (*iter))
+  new_ctx.values->remove (*iter);
 
   return NULL_TREE;
 }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-89652.C.jj 2019-03-11 
15:14:21.877561575 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-89652.C2019-03-11 
15:16:11.962763933 +0100
@@ -0,0 +1,36 @@
+// PR c++/89652
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+template  constexpr auto foo (T ) { return e.foo (); }
+template  constexpr auto bar (T ) { return foo (e); }
+template  struct A { typedef T a[N]; };
+template  struct B {
+  typedef T *b;
+  typename A::a d;
+  constexpr b foo () { return d; }
+};
+template  struct C { long m; };
+struct D { long n; };
+template  struct E {
+  B, 1>::b p;
+  constexpr D operator* () { return {p->m}; }
+  constexpr E operator++ (int) { auto a{*this}; ++p; return a; }
+};
+template 
+constexpr bool operator!= (E a, E) { return a.p; }
+template 
+constexpr auto baz (B s, B)
+{
+  B t{};
+  auto q{foo (t)};
+  using u = E;
+  auto v = u{bar (s)};
+  auto w = u{};
+  while (v != w)
+*q++ = *v++;
+  return t;
+}
+constexpr auto a = B, 5>{};
+auto b = B{};
+auto c = baz (a, b);

Jakub