Re: [PATCH] c++: Don't cache calls which rethrow etc. [PR126508]
Jason Merrill <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
On 7/30/26 8:49 AM, Jakub Jelinek wrote: > Hi! > > The first 3 testcase below are miscompiled, we happily cache > calls during constant evaluation which don't depend just on their > arguments, but also on the current exceptions (uncaught or caught). > If we decide to cache such functions and then try to evaluate them > with different uncaught/caught exceptions (or none), we can get wrong > results. > We already don't cache calls which allocate and don't free all heap > allocations, or free some heap allocations they haven't allocated, > or which call (right now any) metafunctions, or have exited through > exception, or aren't constant. > This patch just adds the rethrow/__builtin_uncaught_exceptions/ > __builtin_current_exception calls to the set of non-cacheable operations > (to be precise, e.g. rethrow would be safe to cache if we can prove > that the current exception was always thrown from within that function, > ditto __builtin_current_exception, but it is hard to figure out). > The last testcase attempts to check if we don't need something similar > also for __builtin_eh_ptr_adjust_ref, but the call to foo for some reason > isn't cached and so I don't have a proof we need to handle it too. > > So far lightly tested, ok for trunk/16.2 if it passes full > bootstrap/regtest? OK, though we might rename metafns_called to something like state_dependent and mention EH in its comment. That can be a trunk-only followup. > 2026-07-30 Jakub Jelinek <[email protected]> > > PR c++/126508 > * constexpr.cc (cxx_eval_cxa_builtin_fn): Avoid caching > calls which rethrow or call __builtin_uncaught_exceptions > or __builtin_current_exception. > > * g++.dg/cpp26/constexpr-eh20.C: New test. > * g++.dg/cpp26/constexpr-eh21.C: New test. > * g++.dg/cpp26/constexpr-eh22.C: New test. > * g++.dg/cpp26/constexpr-eh23.C: New test. > > --- gcc/cp/constexpr.cc.jj 2026-07-27 18:51:44.636642818 +0200 > +++ gcc/cp/constexpr.cc 2026-07-30 14:03:20.410583194 +0200 > @@ -2118,6 +2118,9 @@ cxx_eval_cxa_builtin_fn (const constexpr > DECL_EXCEPTION_REFCOUNT (arg) > = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node); > ++ctx->global->uncaught_exceptions; > + /* Don't cache calls which rethrow, they depend on the current > + exception which might be caught in the caller. */ > + ctx->global->metafns_called = true; > *jump_target = arg; > return void_node; > case CXA_BAD_CAST: > @@ -2196,6 +2199,10 @@ cxx_eval_cxa_builtin_fn (const constexpr > *non_constant_p = true; > return call; > } > + /* Don't cache calls which call __builtin_uncaught_exceptions (), > + they depend on the current uncaught exceptions which might > + be the state from their caller. */ > + ctx->global->metafns_called = true; > return build_int_cst (integer_type_node, > ctx->global->uncaught_exceptions); > case BUILTIN_CURRENT_EXCEPTION: > @@ -2247,6 +2254,10 @@ cxx_eval_cxa_builtin_fn (const constexpr > size_one_node); > arg = fold_convert (ptr_type_node, build_address (arg)); > } > + /* Don't cache calls which call __builtin_current_exception (), > + they depend on the current exception which might be caught > + in the caller. */ > + ctx->global->metafns_called = true; > return build_constructor_single (TREE_TYPE (decl), fld, arg); > } > case STD_RETHROW_EXCEPTION: > --- gcc/testsuite/g++.dg/cpp26/constexpr-eh20.C.jj 2026-07-30 13:04:03.278524380 +0200 > +++ gcc/testsuite/g++.dg/cpp26/constexpr-eh20.C 2026-07-30 13:03:39.287820983 +0200 > @@ -0,0 +1,31 @@ > +// PR c++/126508 > +// { dg-do compile { target c++26 } } > + > +constexpr int > +foo () > +{ > + try > + { > + throw; > + } > + catch (const int x) > + { > + return x; > + } > +} > + > +constexpr int > +bar (int x) > +{ > + try > + { > + throw x; > + } > + catch (...) > + { > + return foo (); > + } > +} > + > +static_assert (bar (42) == 42); > +static_assert (bar (43) == 43); > --- gcc/testsuite/g++.dg/cpp26/constexpr-eh21.C.jj 2026-07-30 13:05:39.040340448 +0200 > +++ gcc/testsuite/g++.dg/cpp26/constexpr-eh21.C 2026-07-30 13:09:35.632415397 +0200 > @@ -0,0 +1,43 @@ > +// PR c++/126508 > +// { dg-do compile { target c++26 } } > + > +constexpr int > +foo () > +{ > + return __builtin_uncaught_exceptions (); > +} > + > +constexpr int > +bar () > +{ > + return __builtin_uncaught_exceptions (); > +} > + > +struct A { constexpr A () : a (0) {} constexpr ~A () { if (foo () != a) asm (""); } int a; }; > +struct B { constexpr B () : b (0) {} constexpr ~B () { if (bar () != b) asm (""); } int b; }; > + > +constexpr bool > +baz () > +{ > + { > + A a; > + } > + try > + { > + A a; > + B b; > + a.a = 1; > + b.b = 1; > + throw 42; > + } > + catch (...) > + { > + } > + { > + A a; > + B b; > + } > + return true; > +} > + > +static_assert (baz ()); > --- gcc/testsuite/g++.dg/cpp26/constexpr-eh22.C.jj 2026-07-30 13:15:19.463164518 +0200 > +++ gcc/testsuite/g++.dg/cpp26/constexpr-eh22.C 2026-07-30 13:17:06.057846657 +0200 > @@ -0,0 +1,41 @@ > +// PR c++/126508 > +// { dg-do compile { target c++26 } } > + > +#include <exception> > + > +constexpr bool > +foo () > +{ > + return __builtin_current_exception () != nullptr; > +} > + > +constexpr int > +bar () > +{ > + return __builtin_current_exception () != nullptr; > +} > + > +constexpr bool > +baz () > +{ > + if (foo ()) > + return false; > + try > + { > + throw 42; > + } > + catch (...) > + { > + if (!foo ()) > + return false; > + if (!bar ()) > + return false; > + } > + if (foo ()) > + return false; > + if (bar ()) > + return false; > + return true; > +} > + > +static_assert (baz ()); > --- gcc/testsuite/g++.dg/cpp26/constexpr-eh23.C.jj 2026-07-30 13:28:07.676488300 +0200 > +++ gcc/testsuite/g++.dg/cpp26/constexpr-eh23.C 2026-07-30 13:28:03.177545200 +0200 > @@ -0,0 +1,22 @@ > +// PR c++/126508 > +// { dg-do compile { target c++26 } } > + > +#include <exception> > + > +constexpr std::exception_ptr > +foo (const std::exception_ptr &x) > +{ > + return x; > +} > + > +constexpr bool > +baz () > +{ > + std::exception_ptr a = std::make_exception_ptr (42); > + auto b = foo (a); > + auto c = foo (a); > + auto d = foo (a); > + return true; > +} > + > +static_assert (baz ()); > > Jakub >