Message ID | 20190307215255.GI26967@redhat.com |
---|---|
State | New |
Headers | show |
Series | C++ PATCH for c++/89612 - ICE with member friend template with noexcept | expand |
Ping. On Thu, Mar 07, 2019 at 04:52:55PM -0500, Marek Polacek wrote: > This was one of those PRs where the more you poke, the more ICEs turn up. > This patch fixes the ones I could find. The original problem was that > maybe_instantiate_noexcept got a TEMPLATE_DECL created for the member > friend template in do_friend. Its noexcept-specification was deferred, > so we went to the block with push_access_scope, but that crashes on a > TEMPLATE_DECL. One approach could be to somehow not defer noexcept-specs > for friend templates, I guess, but I didn't want to do that. > > So the approach I did take in the end was to handle TEMPLATE_DECLs in > maybe_instantiate_noexcept. > > That broke in register_parameter_specializations but we don't need this > code anyway, so let's do away with it -- the current_class_{ref,ptr} > code is enough to fix the PR that register_parameter_specializations was > introduced for. > > Another issue was that since here we are instantiating a deferred noexcept, > in a instantiate_class_template context, processing_template_decl was 0. > Let's pretend it's on for the tsubst purposes here, and for build_noexcept_spec > too, so that the *_dependent_expression_p functions work. > > Lastly, I found an invalid testcase that was breaking because a template code > leaked to constexpr functions. This I fixed similarly to the recent explicit > PR fix (r269131). > > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > 2019-03-07 Marek Polacek <polacek@redhat.com> > > PR c++/89612 - ICE with member friend template with noexcept. > * except.c (build_noexcept_spec): Call instantiate_non_dependent_expr > before perform_implicit_conversion_flags. Add processing_template_decl > sentinel. > * pt.c (maybe_instantiate_noexcept): For function templates, use their > template result (function decl). Don't set up local specializations. > Temporarily turn on processing_template_decl. Update the template type > too. > > * g++.dg/cpp0x/noexcept36.C: New test. > * g++.dg/cpp1y/noexcept1.C: New test. > * g++.dg/cpp1z/noexcept-type21.C: New test. > > diff --git gcc/cp/except.c gcc/cp/except.c > index 139e871d7a7..d97b8d40542 100644 > --- gcc/cp/except.c > +++ gcc/cp/except.c > @@ -1285,10 +1285,13 @@ build_noexcept_spec (tree expr, tsubst_flags_t complain) > if (TREE_CODE (expr) != DEFERRED_NOEXCEPT > && !value_dependent_expression_p (expr)) > { > + expr = instantiate_non_dependent_expr_sfinae (expr, complain); > + /* Don't let perform_implicit_conversion_flags create more template > + codes. */ > + processing_template_decl_sentinel s; > expr = perform_implicit_conversion_flags (boolean_type_node, expr, > complain, > LOOKUP_NORMAL); > - expr = instantiate_non_dependent_expr (expr); > expr = cxx_constant_value (expr); > } > if (TREE_CODE (expr) == INTEGER_CST) > diff --git gcc/cp/pt.c gcc/cp/pt.c > index 906cfe0a58c..44a2c4606f8 100644 > --- gcc/cp/pt.c > +++ gcc/cp/pt.c > @@ -24174,6 +24174,17 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) > > if (DECL_CLONED_FUNCTION_P (fn)) > fn = DECL_CLONED_FUNCTION (fn); > + > + tree orig_fn = NULL_TREE; > + /* For a member friend template we can get a TEMPLATE_DECL. Let's use > + its FUNCTION_DECL for the rest of this function -- push_access_scope > + doesn't accept TEMPLATE_DECLs. */ > + if (DECL_FUNCTION_TEMPLATE_P (fn)) > + { > + orig_fn = fn; > + fn = DECL_TEMPLATE_RESULT (fn); > + } > + > fntype = TREE_TYPE (fn); > spec = TYPE_RAISES_EXCEPTIONS (fntype); > > @@ -24204,37 +24215,41 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) > push_deferring_access_checks (dk_no_deferred); > input_location = DECL_SOURCE_LOCATION (fn); > > - /* A new stack interferes with pop_access_scope. */ > - { > - /* Set up the list of local specializations. */ > - local_specialization_stack lss (lss_copy); > - > - tree save_ccp = current_class_ptr; > - tree save_ccr = current_class_ref; > - /* If needed, set current_class_ptr for the benefit of > - tsubst_copy/PARM_DECL. */ > - tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn)); > - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl)) > - { > - tree this_parm = DECL_ARGUMENTS (tdecl); > - current_class_ptr = NULL_TREE; > - current_class_ref = cp_build_fold_indirect_ref (this_parm); > - current_class_ptr = this_parm; > - } > + tree save_ccp = current_class_ptr; > + tree save_ccr = current_class_ref; > + /* If needed, set current_class_ptr for the benefit of > + tsubst_copy/PARM_DECL. */ > + tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn)); > + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl)) > + { > + tree this_parm = DECL_ARGUMENTS (tdecl); > + current_class_ptr = NULL_TREE; > + current_class_ref = cp_build_fold_indirect_ref (this_parm); > + current_class_ptr = this_parm; > + } > > - /* Create substitution entries for the parameters. */ > - register_parameter_specializations (tdecl, fn); > + /* If this function is represented by a TEMPLATE_DECL, then > + the deferred noexcept-specification might still contain > + dependent types, even after substitution. And we need the > + dependency check functions to work in build_noexcept_spec. */ > + if (orig_fn) > + ++processing_template_decl; > > - /* Do deferred instantiation of the noexcept-specifier. */ > - noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex), > - DEFERRED_NOEXCEPT_ARGS (noex), > - tf_warning_or_error, fn, > - /*function_p=*/false, > - /*i_c_e_p=*/true); > - current_class_ptr = save_ccp; > - current_class_ref = save_ccr; > - spec = build_noexcept_spec (noex, tf_warning_or_error); > - } > + /* Do deferred instantiation of the noexcept-specifier. */ > + noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex), > + DEFERRED_NOEXCEPT_ARGS (noex), > + tf_warning_or_error, fn, > + /*function_p=*/false, > + /*i_c_e_p=*/true); > + > + current_class_ptr = save_ccp; > + current_class_ref = save_ccr; > + > + /* Build up the noexcept-specification. */ > + spec = build_noexcept_spec (noex, tf_warning_or_error); > + > + if (orig_fn) > + --processing_template_decl; > > pop_deferring_access_checks (); > pop_access_scope (fn); > @@ -24250,6 +24265,8 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) > return false; > > TREE_TYPE (fn) = build_exception_variant (fntype, spec); > + if (orig_fn) > + TREE_TYPE (orig_fn) = TREE_TYPE (fn); > } > > FOR_EACH_CLONE (clone, fn) > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept36.C gcc/testsuite/g++.dg/cpp0x/noexcept36.C > new file mode 100644 > index 00000000000..ecab59df694 > --- /dev/null > +++ gcc/testsuite/g++.dg/cpp0x/noexcept36.C > @@ -0,0 +1,19 @@ > +// PR c++/89612 > +// { dg-do compile { target c++11 } } > + > +template <typename> > +struct C { > + template <int N> > + friend int foo() noexcept(N); > + > + template <int N> > + friend int foo2() noexcept(N); // { dg-error "different exception" } > +}; > + > +template <int N> > +int foo() noexcept(N); > + > +template <int N> > +int foo2() noexcept(N + 1); > + > +C<int> c; > diff --git gcc/testsuite/g++.dg/cpp1y/noexcept1.C gcc/testsuite/g++.dg/cpp1y/noexcept1.C > new file mode 100644 > index 00000000000..2344b1ba92c > --- /dev/null > +++ gcc/testsuite/g++.dg/cpp1y/noexcept1.C > @@ -0,0 +1,13 @@ > +// PR c++/89612 > +// { dg-do compile { target c++14 } } > + > +template <int> bool b; > + > +template <typename> > +struct C { > + template <typename> friend int foo() noexcept(b<1>); // { dg-error "not usable in a constant expression|different exception specifier" } > +}; > + > +template <typename> int foo() noexcept(b<1>); > + > +auto a = C<int>(); > diff --git gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C > new file mode 100644 > index 00000000000..d0a61d95e87 > --- /dev/null > +++ gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C > @@ -0,0 +1,16 @@ > +// PR c++/89612 > +// { dg-do compile { target c++17 } } > + > +template <typename a> using b = typename a ::c; > +template <typename> bool d; > +template <typename, typename> struct e { > + template <typename f, typename g> e(f, g) {} > + template <typename h, typename i, typename j> > + friend auto k(h &&, const j &, i &&) noexcept(d<b<h>, h> &&d<b<i>, i>); > +}; > +template <typename l, typename m> e(l, m)->e<l, m>; > +template <typename l, typename m, typename j> > +auto k(l &&, const j &, m &&) noexcept(d<b<l>, l> &&d<b<m>, m>); > +int main() { > + e(0, [] {}); > +} Marek
On 3/7/19 4:52 PM, Marek Polacek wrote: > This was one of those PRs where the more you poke, the more ICEs turn up. > This patch fixes the ones I could find. The original problem was that > maybe_instantiate_noexcept got a TEMPLATE_DECL created for the member > friend template in do_friend. Its noexcept-specification was deferred, > so we went to the block with push_access_scope, but that crashes on a > TEMPLATE_DECL. One approach could be to somehow not defer noexcept-specs > for friend templates, I guess, but I didn't want to do that. How does it make sense to instantiate the noexcept-specifier of a template? We should only get there for fully-instantiated function decls. > Lastly, I found an invalid testcase that was breaking because a template code > leaked to constexpr functions. This I fixed similarly to the recent explicit > PR fix (r269131). This spot should probably also use build_converted_constant_expr. Jason
On Thu, Mar 14, 2019 at 04:22:41PM -0400, Jason Merrill wrote: > On 3/7/19 4:52 PM, Marek Polacek wrote: > > This was one of those PRs where the more you poke, the more ICEs turn up. > > This patch fixes the ones I could find. The original problem was that > > maybe_instantiate_noexcept got a TEMPLATE_DECL created for the member > > friend template in do_friend. Its noexcept-specification was deferred, > > so we went to the block with push_access_scope, but that crashes on a > > TEMPLATE_DECL. One approach could be to somehow not defer noexcept-specs > > for friend templates, I guess, but I didn't want to do that. > > How does it make sense to instantiate the noexcept-specifier of a template? > We should only get there for fully-instantiated function decls. Hmm, but duplicate_decls calls check_redeclaration_exception_specification even for DECL_FUNCTION_TEMPLATE_Ps. It's likely I got this wrong, but I thought template friends are somewhat special, and they are wrapped in a TEMPLATE_DECL even when it's not an instantiation. If I have this template <typename> class C { int n; template <int N> friend int foo(C); }; template <int N> int foo(C<int> c) { return c.n; } void g () { C<int> c; foo<0>(c); } then we call instantiate_class_template for C, which calls tsubst_friend_function on the TEMPLATE_DECL foo, but that remains a TEMPLATE_DECL, unlike for member template functions. Here's a related patch: https://gcc.gnu.org/ml/gcc-patches/2014-01/msg01918.html Note the crash happens in tsubst_friend_function. I wouldn't know when to check the noexcept-specifier of such a TEMPLATE_DECL for a template friend if not there. > > Lastly, I found an invalid testcase that was breaking because a template code > > leaked to constexpr functions. This I fixed similarly to the recent explicit > > PR fix (r269131). > > This spot should probably also use build_converted_constant_expr. Ok, I'll address this. Marek
On 3/19/19 11:45 AM, Marek Polacek wrote: > On Thu, Mar 14, 2019 at 04:22:41PM -0400, Jason Merrill wrote: >> On 3/7/19 4:52 PM, Marek Polacek wrote: >>> This was one of those PRs where the more you poke, the more ICEs turn up. >>> This patch fixes the ones I could find. The original problem was that >>> maybe_instantiate_noexcept got a TEMPLATE_DECL created for the member >>> friend template in do_friend. Its noexcept-specification was deferred, >>> so we went to the block with push_access_scope, but that crashes on a >>> TEMPLATE_DECL. One approach could be to somehow not defer noexcept-specs >>> for friend templates, I guess, but I didn't want to do that. >> How does it make sense to instantiate the noexcept-specifier of a template? >> We should only get there for fully-instantiated function decls. > > Hmm, but duplicate_decls calls check_redeclaration_exception_specification even > for DECL_FUNCTION_TEMPLATE_Ps. > ... > Note the crash happens in tsubst_friend_function. I wouldn't know when to > check the noexcept-specifier of such a TEMPLATE_DECL for a template friend > if not there. Hmm, true, I guess we do need to do a partial instantiation of the noexcept-specifier in order to compare it. > That broke in register_parameter_specializations but we don't need this > code anyway, so let's do away with it -- the current_class_{ref,ptr} > code is enough to fix the PR that register_parameter_specializations was > introduced for. What about uses of non-'this' parameters in the noexcept-specification? template <typename T> struct C { template <int N> friend void foo(T t) noexcept(sizeof(decltype(t)) > 1); }; template <int N> void foo(int i) noexcept { } C<int> c; >>> Lastly, I found an invalid testcase that was breaking because a template code >>> leaked to constexpr functions. This I fixed similarly to the recent explicit >>> PR fix (r269131). >> >> This spot should probably also use build_converted_constant_expr. > > Ok, I'll address this. I'm finding this repeated pattern awkward. Earlier you changed check_narrowing to use maybe_constant_value instead of fold_non_dependent_expr, but perhaps whatever that fixed should have been fixed instead with a processing_template_decl_sentinel in the enclosing code that already instantiated the expression. That ought to avoid any need to change this spot or r269131. Jason
On Thu, Mar 21, 2019 at 04:00:41PM -0400, Jason Merrill wrote: > On 3/19/19 11:45 AM, Marek Polacek wrote: > > On Thu, Mar 14, 2019 at 04:22:41PM -0400, Jason Merrill wrote: > > > On 3/7/19 4:52 PM, Marek Polacek wrote: > > > > This was one of those PRs where the more you poke, the more ICEs turn up. > > > > This patch fixes the ones I could find. The original problem was that > > > > maybe_instantiate_noexcept got a TEMPLATE_DECL created for the member > > > > friend template in do_friend. Its noexcept-specification was deferred, > > > > so we went to the block with push_access_scope, but that crashes on a > > > > TEMPLATE_DECL. One approach could be to somehow not defer noexcept-specs > > > > for friend templates, I guess, but I didn't want to do that. > > > > How does it make sense to instantiate the noexcept-specifier of a template? > > > We should only get there for fully-instantiated function decls. > > > > Hmm, but duplicate_decls calls check_redeclaration_exception_specification even > > for DECL_FUNCTION_TEMPLATE_Ps. > > ... > > Note the crash happens in tsubst_friend_function. I wouldn't know when to > > check the noexcept-specifier of such a TEMPLATE_DECL for a template friend > > if not there. > > Hmm, true, I guess we do need to do a partial instantiation of the > noexcept-specifier in order to compare it. *nod* > > That broke in register_parameter_specializations but we don't need this > > code anyway, so let's do away with it -- the current_class_{ref,ptr} > > code is enough to fix the PR that register_parameter_specializations was > > introduced for. > > What about uses of non-'this' parameters in the noexcept-specification? > > template <typename T> > struct C { > template <int N> > friend void foo(T t) noexcept(sizeof(decltype(t)) > 1); > }; > > template <int N> > void foo(int i) noexcept { } > > C<int> c; Still works. I extended the test to see if we detect the scenario when the noexcept-specifiers don't match, and we do. It's noexcept39.C. > > > > Lastly, I found an invalid testcase that was breaking because a template code > > > > leaked to constexpr functions. This I fixed similarly to the recent explicit > > > > PR fix (r269131). > > > > > > This spot should probably also use build_converted_constant_expr. > > > > Ok, I'll address this. > > I'm finding this repeated pattern awkward. Earlier you changed > check_narrowing to use maybe_constant_value instead of > fold_non_dependent_expr, but perhaps whatever that fixed should have been > fixed instead with a processing_template_decl_sentinel in the enclosing code > that already instantiated the expression. That ought to avoid any need to > change this spot or r269131. So this also came up in the other patch. Why don't I drop this part (and the noexcept1.C test) and open a new PR for this issue, so that we don't conflate two problems? The following patch fixes the original issue. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-03-28 Marek Polacek <polacek@redhat.com> PR c++/89612 - ICE with member friend template with noexcept. * pt.c (maybe_instantiate_noexcept): For function templates, use their template result (function decl). Don't set up local specializations. Temporarily turn on processing_template_decl. Update the template type too. * g++.dg/cpp0x/noexcept38.C: New test. * g++.dg/cpp0x/noexcept39.C: New test. * g++.dg/cpp1z/noexcept-type21.C: New test. diff --git gcc/cp/pt.c gcc/cp/pt.c index 05d5371d8a6..fa30a7f00c8 100644 --- gcc/cp/pt.c +++ gcc/cp/pt.c @@ -24192,6 +24192,17 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) if (DECL_CLONED_FUNCTION_P (fn)) fn = DECL_CLONED_FUNCTION (fn); + + tree orig_fn = NULL_TREE; + /* For a member friend template we can get a TEMPLATE_DECL. Let's use + its FUNCTION_DECL for the rest of this function -- push_access_scope + doesn't accept TEMPLATE_DECLs. */ + if (DECL_FUNCTION_TEMPLATE_P (fn)) + { + orig_fn = fn; + fn = DECL_TEMPLATE_RESULT (fn); + } + fntype = TREE_TYPE (fn); spec = TYPE_RAISES_EXCEPTIONS (fntype); @@ -24228,37 +24239,41 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) push_deferring_access_checks (dk_no_deferred); input_location = DECL_SOURCE_LOCATION (fn); - /* A new stack interferes with pop_access_scope. */ - { - /* Set up the list of local specializations. */ - local_specialization_stack lss (lss_copy); - - tree save_ccp = current_class_ptr; - tree save_ccr = current_class_ref; - /* If needed, set current_class_ptr for the benefit of - tsubst_copy/PARM_DECL. */ - tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn)); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl)) - { - tree this_parm = DECL_ARGUMENTS (tdecl); - current_class_ptr = NULL_TREE; - current_class_ref = cp_build_fold_indirect_ref (this_parm); - current_class_ptr = this_parm; - } + tree save_ccp = current_class_ptr; + tree save_ccr = current_class_ref; + /* If needed, set current_class_ptr for the benefit of + tsubst_copy/PARM_DECL. */ + tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn)); + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl)) + { + tree this_parm = DECL_ARGUMENTS (tdecl); + current_class_ptr = NULL_TREE; + current_class_ref = cp_build_fold_indirect_ref (this_parm); + current_class_ptr = this_parm; + } - /* Create substitution entries for the parameters. */ - register_parameter_specializations (tdecl, fn); + /* If this function is represented by a TEMPLATE_DECL, then + the deferred noexcept-specification might still contain + dependent types, even after substitution. And we need the + dependency check functions to work in build_noexcept_spec. */ + if (orig_fn) + ++processing_template_decl; - /* Do deferred instantiation of the noexcept-specifier. */ - noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex), - DEFERRED_NOEXCEPT_ARGS (noex), - tf_warning_or_error, fn, - /*function_p=*/false, - /*i_c_e_p=*/true); - current_class_ptr = save_ccp; - current_class_ref = save_ccr; - spec = build_noexcept_spec (noex, tf_warning_or_error); - } + /* Do deferred instantiation of the noexcept-specifier. */ + noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex), + DEFERRED_NOEXCEPT_ARGS (noex), + tf_warning_or_error, fn, + /*function_p=*/false, + /*i_c_e_p=*/true); + + current_class_ptr = save_ccp; + current_class_ref = save_ccr; + + /* Build up the noexcept-specification. */ + spec = build_noexcept_spec (noex, tf_warning_or_error); + + if (orig_fn) + --processing_template_decl; pop_deferring_access_checks (); pop_access_scope (fn); @@ -24278,6 +24293,8 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) } TREE_TYPE (fn) = build_exception_variant (fntype, spec); + if (orig_fn) + TREE_TYPE (orig_fn) = TREE_TYPE (fn); } FOR_EACH_CLONE (clone, fn) diff --git gcc/testsuite/g++.dg/cpp0x/noexcept38.C gcc/testsuite/g++.dg/cpp0x/noexcept38.C new file mode 100644 index 00000000000..ecab59df694 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept38.C @@ -0,0 +1,19 @@ +// PR c++/89612 +// { dg-do compile { target c++11 } } + +template <typename> +struct C { + template <int N> + friend int foo() noexcept(N); + + template <int N> + friend int foo2() noexcept(N); // { dg-error "different exception" } +}; + +template <int N> +int foo() noexcept(N); + +template <int N> +int foo2() noexcept(N + 1); + +C<int> c; diff --git gcc/testsuite/g++.dg/cpp0x/noexcept39.C gcc/testsuite/g++.dg/cpp0x/noexcept39.C new file mode 100644 index 00000000000..fbebbed5e4c --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept39.C @@ -0,0 +1,19 @@ +// PR c++/89612 +// { dg-do compile { target c++11 } } + +template <typename T> +struct C { + template <int N> + friend void foo(T t) noexcept(sizeof(decltype(t)) > 1); + + template <int N> + friend void foo2(T t) noexcept(sizeof(decltype(t)) < 1); // { dg-error "different exception" } +}; + +template <int N> +void foo(int i) noexcept { } + +template <int N> +void foo2(int i) noexcept { } + +C<int> c; diff --git gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C new file mode 100644 index 00000000000..d0a61d95e87 --- /dev/null +++ gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C @@ -0,0 +1,16 @@ +// PR c++/89612 +// { dg-do compile { target c++17 } } + +template <typename a> using b = typename a ::c; +template <typename> bool d; +template <typename, typename> struct e { + template <typename f, typename g> e(f, g) {} + template <typename h, typename i, typename j> + friend auto k(h &&, const j &, i &&) noexcept(d<b<h>, h> &&d<b<i>, i>); +}; +template <typename l, typename m> e(l, m)->e<l, m>; +template <typename l, typename m, typename j> +auto k(l &&, const j &, m &&) noexcept(d<b<l>, l> &&d<b<m>, m>); +int main() { + e(0, [] {}); +}
On 3/28/19 2:59 PM, Marek Polacek wrote: > On Thu, Mar 21, 2019 at 04:00:41PM -0400, Jason Merrill wrote: >> On 3/19/19 11:45 AM, Marek Polacek wrote: >>> On Thu, Mar 14, 2019 at 04:22:41PM -0400, Jason Merrill wrote: >>>> On 3/7/19 4:52 PM, Marek Polacek wrote: >>>>> This was one of those PRs where the more you poke, the more ICEs turn up. >>>>> This patch fixes the ones I could find. The original problem was that >>>>> maybe_instantiate_noexcept got a TEMPLATE_DECL created for the member >>>>> friend template in do_friend. Its noexcept-specification was deferred, >>>>> so we went to the block with push_access_scope, but that crashes on a >>>>> TEMPLATE_DECL. One approach could be to somehow not defer noexcept-specs >>>>> for friend templates, I guess, but I didn't want to do that. >> >>>> How does it make sense to instantiate the noexcept-specifier of a template? >>>> We should only get there for fully-instantiated function decls. >>> >>> Hmm, but duplicate_decls calls check_redeclaration_exception_specification even >>> for DECL_FUNCTION_TEMPLATE_Ps. >>> ... >>> Note the crash happens in tsubst_friend_function. I wouldn't know when to >>> check the noexcept-specifier of such a TEMPLATE_DECL for a template friend >>> if not there. >> >> Hmm, true, I guess we do need to do a partial instantiation of the >> noexcept-specifier in order to compare it. > > *nod* > >>> That broke in register_parameter_specializations but we don't need this >>> code anyway, so let's do away with it -- the current_class_{ref,ptr} >>> code is enough to fix the PR that register_parameter_specializations was >>> introduced for. >> >> What about uses of non-'this' parameters in the noexcept-specification? >> >> template <typename T> >> struct C { >> template <int N> >> friend void foo(T t) noexcept(sizeof(decltype(t)) > 1); >> }; >> >> template <int N> >> void foo(int i) noexcept { } >> >> C<int> c; > > Still works. I extended the test to see if we detect the scenario when the > noexcept-specifiers don't match, and we do. It's noexcept39.C. > >>>>> Lastly, I found an invalid testcase that was breaking because a template code >>>>> leaked to constexpr functions. This I fixed similarly to the recent explicit >>>>> PR fix (r269131). >>>> >>>> This spot should probably also use build_converted_constant_expr. >>> >>> Ok, I'll address this. >> >> I'm finding this repeated pattern awkward. Earlier you changed >> check_narrowing to use maybe_constant_value instead of >> fold_non_dependent_expr, but perhaps whatever that fixed should have been >> fixed instead with a processing_template_decl_sentinel in the enclosing code >> that already instantiated the expression. That ought to avoid any need to >> change this spot or r269131. > > So this also came up in the other patch. Why don't I drop this part (and the > noexcept1.C test) and open a new PR for this issue, so that we don't conflate > two problems? The following patch fixes the original issue. > > Bootstrapped/regtested on x86_64-linux, ok for trunk? OK. Jason > > 2019-03-28 Marek Polacek <polacek@redhat.com> > > PR c++/89612 - ICE with member friend template with noexcept. > * pt.c (maybe_instantiate_noexcept): For function templates, use their > template result (function decl). Don't set up local specializations. > Temporarily turn on processing_template_decl. Update the template type > too. > > * g++.dg/cpp0x/noexcept38.C: New test. > * g++.dg/cpp0x/noexcept39.C: New test. > * g++.dg/cpp1z/noexcept-type21.C: New test. > > diff --git gcc/cp/pt.c gcc/cp/pt.c > index 05d5371d8a6..fa30a7f00c8 100644 > --- gcc/cp/pt.c > +++ gcc/cp/pt.c > @@ -24192,6 +24192,17 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) > > if (DECL_CLONED_FUNCTION_P (fn)) > fn = DECL_CLONED_FUNCTION (fn); > + > + tree orig_fn = NULL_TREE; > + /* For a member friend template we can get a TEMPLATE_DECL. Let's use > + its FUNCTION_DECL for the rest of this function -- push_access_scope > + doesn't accept TEMPLATE_DECLs. */ > + if (DECL_FUNCTION_TEMPLATE_P (fn)) > + { > + orig_fn = fn; > + fn = DECL_TEMPLATE_RESULT (fn); > + } > + > fntype = TREE_TYPE (fn); > spec = TYPE_RAISES_EXCEPTIONS (fntype); > > @@ -24228,37 +24239,41 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) > push_deferring_access_checks (dk_no_deferred); > input_location = DECL_SOURCE_LOCATION (fn); > > - /* A new stack interferes with pop_access_scope. */ > - { > - /* Set up the list of local specializations. */ > - local_specialization_stack lss (lss_copy); > - > - tree save_ccp = current_class_ptr; > - tree save_ccr = current_class_ref; > - /* If needed, set current_class_ptr for the benefit of > - tsubst_copy/PARM_DECL. */ > - tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn)); > - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl)) > - { > - tree this_parm = DECL_ARGUMENTS (tdecl); > - current_class_ptr = NULL_TREE; > - current_class_ref = cp_build_fold_indirect_ref (this_parm); > - current_class_ptr = this_parm; > - } > + tree save_ccp = current_class_ptr; > + tree save_ccr = current_class_ref; > + /* If needed, set current_class_ptr for the benefit of > + tsubst_copy/PARM_DECL. */ > + tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn)); > + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl)) > + { > + tree this_parm = DECL_ARGUMENTS (tdecl); > + current_class_ptr = NULL_TREE; > + current_class_ref = cp_build_fold_indirect_ref (this_parm); > + current_class_ptr = this_parm; > + } > > - /* Create substitution entries for the parameters. */ > - register_parameter_specializations (tdecl, fn); > + /* If this function is represented by a TEMPLATE_DECL, then > + the deferred noexcept-specification might still contain > + dependent types, even after substitution. And we need the > + dependency check functions to work in build_noexcept_spec. */ > + if (orig_fn) > + ++processing_template_decl; > > - /* Do deferred instantiation of the noexcept-specifier. */ > - noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex), > - DEFERRED_NOEXCEPT_ARGS (noex), > - tf_warning_or_error, fn, > - /*function_p=*/false, > - /*i_c_e_p=*/true); > - current_class_ptr = save_ccp; > - current_class_ref = save_ccr; > - spec = build_noexcept_spec (noex, tf_warning_or_error); > - } > + /* Do deferred instantiation of the noexcept-specifier. */ > + noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex), > + DEFERRED_NOEXCEPT_ARGS (noex), > + tf_warning_or_error, fn, > + /*function_p=*/false, > + /*i_c_e_p=*/true); > + > + current_class_ptr = save_ccp; > + current_class_ref = save_ccr; > + > + /* Build up the noexcept-specification. */ > + spec = build_noexcept_spec (noex, tf_warning_or_error); > + > + if (orig_fn) > + --processing_template_decl; > > pop_deferring_access_checks (); > pop_access_scope (fn); > @@ -24278,6 +24293,8 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) > } > > TREE_TYPE (fn) = build_exception_variant (fntype, spec); > + if (orig_fn) > + TREE_TYPE (orig_fn) = TREE_TYPE (fn); > } > > FOR_EACH_CLONE (clone, fn) > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept38.C gcc/testsuite/g++.dg/cpp0x/noexcept38.C > new file mode 100644 > index 00000000000..ecab59df694 > --- /dev/null > +++ gcc/testsuite/g++.dg/cpp0x/noexcept38.C > @@ -0,0 +1,19 @@ > +// PR c++/89612 > +// { dg-do compile { target c++11 } } > + > +template <typename> > +struct C { > + template <int N> > + friend int foo() noexcept(N); > + > + template <int N> > + friend int foo2() noexcept(N); // { dg-error "different exception" } > +}; > + > +template <int N> > +int foo() noexcept(N); > + > +template <int N> > +int foo2() noexcept(N + 1); > + > +C<int> c; > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept39.C gcc/testsuite/g++.dg/cpp0x/noexcept39.C > new file mode 100644 > index 00000000000..fbebbed5e4c > --- /dev/null > +++ gcc/testsuite/g++.dg/cpp0x/noexcept39.C > @@ -0,0 +1,19 @@ > +// PR c++/89612 > +// { dg-do compile { target c++11 } } > + > +template <typename T> > +struct C { > + template <int N> > + friend void foo(T t) noexcept(sizeof(decltype(t)) > 1); > + > + template <int N> > + friend void foo2(T t) noexcept(sizeof(decltype(t)) < 1); // { dg-error "different exception" } > +}; > + > +template <int N> > +void foo(int i) noexcept { } > + > +template <int N> > +void foo2(int i) noexcept { } > + > +C<int> c; > diff --git gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C > new file mode 100644 > index 00000000000..d0a61d95e87 > --- /dev/null > +++ gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C > @@ -0,0 +1,16 @@ > +// PR c++/89612 > +// { dg-do compile { target c++17 } } > + > +template <typename a> using b = typename a ::c; > +template <typename> bool d; > +template <typename, typename> struct e { > + template <typename f, typename g> e(f, g) {} > + template <typename h, typename i, typename j> > + friend auto k(h &&, const j &, i &&) noexcept(d<b<h>, h> &&d<b<i>, i>); > +}; > +template <typename l, typename m> e(l, m)->e<l, m>; > +template <typename l, typename m, typename j> > +auto k(l &&, const j &, m &&) noexcept(d<b<l>, l> &&d<b<m>, m>); > +int main() { > + e(0, [] {}); > +} >
diff --git gcc/cp/except.c gcc/cp/except.c index 139e871d7a7..d97b8d40542 100644 --- gcc/cp/except.c +++ gcc/cp/except.c @@ -1285,10 +1285,13 @@ build_noexcept_spec (tree expr, tsubst_flags_t complain) if (TREE_CODE (expr) != DEFERRED_NOEXCEPT && !value_dependent_expression_p (expr)) { + expr = instantiate_non_dependent_expr_sfinae (expr, complain); + /* Don't let perform_implicit_conversion_flags create more template + codes. */ + processing_template_decl_sentinel s; expr = perform_implicit_conversion_flags (boolean_type_node, expr, complain, LOOKUP_NORMAL); - expr = instantiate_non_dependent_expr (expr); expr = cxx_constant_value (expr); } if (TREE_CODE (expr) == INTEGER_CST) diff --git gcc/cp/pt.c gcc/cp/pt.c index 906cfe0a58c..44a2c4606f8 100644 --- gcc/cp/pt.c +++ gcc/cp/pt.c @@ -24174,6 +24174,17 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) if (DECL_CLONED_FUNCTION_P (fn)) fn = DECL_CLONED_FUNCTION (fn); + + tree orig_fn = NULL_TREE; + /* For a member friend template we can get a TEMPLATE_DECL. Let's use + its FUNCTION_DECL for the rest of this function -- push_access_scope + doesn't accept TEMPLATE_DECLs. */ + if (DECL_FUNCTION_TEMPLATE_P (fn)) + { + orig_fn = fn; + fn = DECL_TEMPLATE_RESULT (fn); + } + fntype = TREE_TYPE (fn); spec = TYPE_RAISES_EXCEPTIONS (fntype); @@ -24204,37 +24215,41 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) push_deferring_access_checks (dk_no_deferred); input_location = DECL_SOURCE_LOCATION (fn); - /* A new stack interferes with pop_access_scope. */ - { - /* Set up the list of local specializations. */ - local_specialization_stack lss (lss_copy); - - tree save_ccp = current_class_ptr; - tree save_ccr = current_class_ref; - /* If needed, set current_class_ptr for the benefit of - tsubst_copy/PARM_DECL. */ - tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn)); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl)) - { - tree this_parm = DECL_ARGUMENTS (tdecl); - current_class_ptr = NULL_TREE; - current_class_ref = cp_build_fold_indirect_ref (this_parm); - current_class_ptr = this_parm; - } + tree save_ccp = current_class_ptr; + tree save_ccr = current_class_ref; + /* If needed, set current_class_ptr for the benefit of + tsubst_copy/PARM_DECL. */ + tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn)); + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl)) + { + tree this_parm = DECL_ARGUMENTS (tdecl); + current_class_ptr = NULL_TREE; + current_class_ref = cp_build_fold_indirect_ref (this_parm); + current_class_ptr = this_parm; + } - /* Create substitution entries for the parameters. */ - register_parameter_specializations (tdecl, fn); + /* If this function is represented by a TEMPLATE_DECL, then + the deferred noexcept-specification might still contain + dependent types, even after substitution. And we need the + dependency check functions to work in build_noexcept_spec. */ + if (orig_fn) + ++processing_template_decl; - /* Do deferred instantiation of the noexcept-specifier. */ - noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex), - DEFERRED_NOEXCEPT_ARGS (noex), - tf_warning_or_error, fn, - /*function_p=*/false, - /*i_c_e_p=*/true); - current_class_ptr = save_ccp; - current_class_ref = save_ccr; - spec = build_noexcept_spec (noex, tf_warning_or_error); - } + /* Do deferred instantiation of the noexcept-specifier. */ + noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex), + DEFERRED_NOEXCEPT_ARGS (noex), + tf_warning_or_error, fn, + /*function_p=*/false, + /*i_c_e_p=*/true); + + current_class_ptr = save_ccp; + current_class_ref = save_ccr; + + /* Build up the noexcept-specification. */ + spec = build_noexcept_spec (noex, tf_warning_or_error); + + if (orig_fn) + --processing_template_decl; pop_deferring_access_checks (); pop_access_scope (fn); @@ -24250,6 +24265,8 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) return false; TREE_TYPE (fn) = build_exception_variant (fntype, spec); + if (orig_fn) + TREE_TYPE (orig_fn) = TREE_TYPE (fn); } FOR_EACH_CLONE (clone, fn) diff --git gcc/testsuite/g++.dg/cpp0x/noexcept36.C gcc/testsuite/g++.dg/cpp0x/noexcept36.C new file mode 100644 index 00000000000..ecab59df694 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept36.C @@ -0,0 +1,19 @@ +// PR c++/89612 +// { dg-do compile { target c++11 } } + +template <typename> +struct C { + template <int N> + friend int foo() noexcept(N); + + template <int N> + friend int foo2() noexcept(N); // { dg-error "different exception" } +}; + +template <int N> +int foo() noexcept(N); + +template <int N> +int foo2() noexcept(N + 1); + +C<int> c; diff --git gcc/testsuite/g++.dg/cpp1y/noexcept1.C gcc/testsuite/g++.dg/cpp1y/noexcept1.C new file mode 100644 index 00000000000..2344b1ba92c --- /dev/null +++ gcc/testsuite/g++.dg/cpp1y/noexcept1.C @@ -0,0 +1,13 @@ +// PR c++/89612 +// { dg-do compile { target c++14 } } + +template <int> bool b; + +template <typename> +struct C { + template <typename> friend int foo() noexcept(b<1>); // { dg-error "not usable in a constant expression|different exception specifier" } +}; + +template <typename> int foo() noexcept(b<1>); + +auto a = C<int>(); diff --git gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C new file mode 100644 index 00000000000..d0a61d95e87 --- /dev/null +++ gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C @@ -0,0 +1,16 @@ +// PR c++/89612 +// { dg-do compile { target c++17 } } + +template <typename a> using b = typename a ::c; +template <typename> bool d; +template <typename, typename> struct e { + template <typename f, typename g> e(f, g) {} + template <typename h, typename i, typename j> + friend auto k(h &&, const j &, i &&) noexcept(d<b<h>, h> &&d<b<i>, i>); +}; +template <typename l, typename m> e(l, m)->e<l, m>; +template <typename l, typename m, typename j> +auto k(l &&, const j &, m &&) noexcept(d<b<l>, l> &&d<b<m>, m>); +int main() { + e(0, [] {}); +}