Here we ICE when substituting a deferred noexcept-specifier, because it contains 'this', a PARM_DECL, in an evaluated context. This is different from "noexcept(noexcept(this))" where the noexcept operator's operand is an unevaluated operand. We crash within tsubst_copy's PARM_DECL handling of a 'this' PARM_DECL: 15488 gcc_assert (cp_unevaluated_operand != 0) It'd be wrong to mess with cp_unevaluated_operand (or current_class_*), and since we only check the expression's constness after substituting in maybe_instantiate_noexcept, one fix would be the following.
This is not just about 'this', as the 87844 test shows: here we also have a parameter whose value we're trying to determine -- it's a template arg of a late-specified return type. Returning the original value in tsubst_copy and leaving the later code to complain seems to work here as well. Just removing the assert should work as well. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-02-20 Marek Polacek <pola...@redhat.com> PR c++/88294 - ICE with non-constant noexcept-specifier. PR c++/87844 - ICE with non-constant template argument. * pt.c (tsubst_copy): Turn gcc_assert into a condition. * g++.dg/cpp0x/noexcept34.C: New test. * g++.dg/cpp1y/auto-fn56.C: New test. diff --git gcc/cp/pt.c gcc/cp/pt.c index 8c5a1b312fc..5286b7dac59 100644 --- gcc/cp/pt.c +++ gcc/cp/pt.c @@ -15484,8 +15484,12 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) /* This can happen for a parameter name used later in a function declaration (such as in a late-specified return type). Just - make a dummy decl, since it's only used for its type. */ - gcc_assert (cp_unevaluated_operand != 0); + make a dummy decl, since it's only used for its type. + We may also end up here in invalid code trying to get a value + of a parameter when parsing a deferred noexcept. Just return + the original value so that we can issue an error later. */ + if (cp_unevaluated_operand == 0) + return t; r = tsubst_decl (t, args, complain); /* Give it the template pattern as its context; its true context hasn't been instantiated yet and this is good enough for diff --git gcc/testsuite/g++.dg/cpp0x/noexcept34.C gcc/testsuite/g++.dg/cpp0x/noexcept34.C new file mode 100644 index 00000000000..495cc260d9b --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept34.C @@ -0,0 +1,29 @@ +// PR c++/88294 +// { dg-do compile { target c++11 } } + +constexpr int foo (bool b) { return b; } + +template<typename> struct A +{ + constexpr int f () { return 0; } + bool b = true; + void g () noexcept (f()) { } // { dg-error ".this. is not a constant expression" } + void g2 () noexcept (this->f()) { } // { dg-error ".this. is not a constant expression" } + void g3 () noexcept (b) { } // { dg-error ".this. is not a constant expression|use of .this." } + void g4 (int i) noexcept (i) { } // { dg-error "use of parameter outside function body" } + void g5 () noexcept (A::f()) { } // { dg-error ".this. is not a constant expression" } + void g6 () noexcept (foo(b)) { } // { dg-error ".this. is not a constant expression|use of .this. in a constant expression" } + void g7 () noexcept (int{f()}) { } // { dg-error ".this. is not a constant expression" } +}; + +int main () +{ + A<int> a; + a.g (); + a.g2 (); + a.g3 (); + a.g4 (1); + a.g5 (); + a.g6 (); + a.g7 (); +} diff --git gcc/testsuite/g++.dg/cpp1y/auto-fn56.C gcc/testsuite/g++.dg/cpp1y/auto-fn56.C new file mode 100644 index 00000000000..ecf8a0b776e --- /dev/null +++ gcc/testsuite/g++.dg/cpp1y/auto-fn56.C @@ -0,0 +1,18 @@ +// PR c++/87844 +// { dg-do compile { target c++14 } } + +struct C { + static constexpr bool call(bool) { return true; } +}; + +template<bool b> +struct B {}; + +auto foo(bool b) { + auto f = [](auto c) -> B<decltype(c)::call(b)> { }; // { dg-error "use of parameter from containing function" } + f(C()); // { dg-error "no match for call" } +} + +int main() { + foo(true); +}