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<tree> save_exprs; new_ctx.save_exprs = &save_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 <ja...@redhat.com> PR c++/89652 * constexpr.c (struct constexpr_ctx): Change save_exprs type from hash_set<tree> to vec<tree>. (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) <case SAVE_EXPR>: 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<tree,tree> *values; /* SAVE_EXPRs that we've seen within the current LOOP_EXPR. NULL if we aren't inside a loop. */ - hash_set<tree> *save_exprs; + vec<tree> *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<tree> save_exprs; + auto_vec<tree, 10> save_exprs; ctx_with_save_exprs.save_exprs = &save_exprs; ctx_with_save_exprs.call = &new_call; @@ -1862,9 +1862,10 @@ cxx_eval_call_expression (const constexp } /* Forget the saved values of the callee's SAVE_EXPRs. */ - for (hash_set<tree>::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<tree> save_exprs; + auto_vec<tree, 10> save_exprs; new_ctx.save_exprs = &save_exprs; do { @@ -4234,9 +4235,11 @@ cxx_eval_loop_expr (const constexpr_ctx } /* Forget saved values of SAVE_EXPRs. */ - for (hash_set<tree>::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<tree>::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) - ctx->save_exprs->add (t); + ctx->save_exprs->safe_push (t); } break; --- gcc/testsuite/g++.dg/cpp1y/constexpr-89652.C.jj 2019-03-13 13:22:51.532678917 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-89652.C 2019-03-13 13:22:51.532678917 +0100 @@ -0,0 +1,36 @@ +// PR c++/89652 +// { dg-do compile { target c++14 } } +// { dg-options "" } + +template <typename T> constexpr auto foo (T &e) { return e.foo (); } +template <typename T> constexpr auto bar (T &e) { return foo (e); } +template <typename T, int N> struct A { typedef T a[N]; }; +template <typename T, unsigned long N> struct B { + typedef T *b; + typename A<T, N>::a d; + constexpr b foo () { return d; } +}; +template <typename> struct C { long m; }; +struct D { long n; }; +template <typename, unsigned long> struct E { + B<C<int>, 1>::b p; + constexpr D operator* () { return {p->m}; } + constexpr E operator++ (int) { auto a{*this}; ++p; return a; } +}; +template <typename T, unsigned long N> +constexpr bool operator!= (E<T, N> a, E<T, N>) { return a.p; } +template <unsigned long N, typename T, unsigned long M> +constexpr auto baz (B<T, M> s, B<D, N>) +{ + B<D, M> t{}; + auto q{foo (t)}; + using u = E<T, M>; + auto v = u{bar (s)}; + auto w = u{}; + while (v != w) + *q++ = *v++; + return t; +} +constexpr auto a = B<C<int>, 5>{}; +auto b = B<D, 0>{}; +auto c = baz (a, b); Jakub