Hi! The following testcases show that the recent cxx_eval_loop_expr fix wasn't sufficient in certain cases, it works well if the SAVE_EXPR contains increment of iterator that is initialized to some constant before the loop, but if we have nested loops and might look up the SAVE_EXPR again (usually by processing the LOOP_EXPR again), we can get stale values.
Fixed by forgetting the saved SAVE_EXPR values at the end of the loop too, not just in between iterations. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2016-03-08 Jakub Jelinek <ja...@redhat.com> PR c++/70135 * constexpr.c (cxx_eval_loop_expr): Forget saved values of SAVE_EXPRs even after the last iteration of the loop. * g++.dg/cpp1y/constexpr-loop4.C: New test. * g++.dg/ubsan/pr70135.C: New test. --- gcc/cp/constexpr.c.jj 2016-03-08 09:01:48.000000000 +0100 +++ gcc/cp/constexpr.c 2016-03-08 16:36:27.192053214 +0100 @@ -3165,21 +3165,21 @@ cxx_eval_loop_expr (const constexpr_ctx constexpr_ctx new_ctx = *ctx; tree body = TREE_OPERAND (t, 0); - while (true) + do { hash_set<tree> save_exprs; new_ctx.save_exprs = &save_exprs; cxx_eval_statement_list (&new_ctx, body, non_constant_p, overflow_p, jump_target); - if (returns (jump_target) || breaks (jump_target) || *non_constant_p) - break; /* 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); } + while (!returns (jump_target) && !breaks (jump_target) && !*non_constant_p); + if (breaks (jump_target)) *jump_target = NULL_TREE; --- gcc/testsuite/g++.dg/cpp1y/constexpr-loop4.C.jj 2016-03-08 16:35:01.352224129 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-loop4.C 2016-03-08 16:34:32.000000000 +0100 @@ -0,0 +1,27 @@ +// { dg-do compile { target c++14 } } + +struct A +{ + int i; +}; + +constexpr bool f() +{ + A ar[5] = { 6, 7, 8, 9, 10 }; + A *ap = ar; + int i = 0, j = 0; + for (j = 0; j < 2; j++) + { + do + *ap++ = A{i}; + while (++i < j * 2 + 2); + } + return (ar[0].i == 0 + && ar[1].i == 1 + && ar[2].i == 2 + && ar[3].i == 3 + && ar[4].i == 10); +} + +#define SA(X) static_assert((X),#X) +SA(f()); --- gcc/testsuite/g++.dg/ubsan/pr70135.C.jj 2016-03-08 16:16:06.863701979 +0100 +++ gcc/testsuite/g++.dg/ubsan/pr70135.C 2016-03-08 16:17:26.850610633 +0100 @@ -0,0 +1,36 @@ +// PR c++/70135 +// { dg-do run } +// { dg-options "-fsanitize=bounds -std=c++14" } + +template <bool... b> +struct S { + static constexpr bool c[] {b...}; + static constexpr auto foo () + { + unsigned long n = 0; + for (unsigned long i = 0; i < sizeof (c); i++) + if (!c[i]) + ++n; + return n; + } + static constexpr auto n = foo () + 1; + static constexpr auto bar () + { + int h = 0; + for (int g = 0, i = 0; g < n; ++g) + { + while (i < sizeof...(b) && c[i++]) + ++h; + h += 64; + } + return h; + } +}; + +int +main () +{ + S <true, false, false, true, true, true, false, true> s; + constexpr auto c = s.bar (); + static_assert (s.bar () == 4 * 64 + 5); +} Jakub