C++ PATCH for c++/89612 - ICE with member friend template with noexcept
Jason Merrill
jason@redhat.com
Thu Mar 28 20:08:00 GMT 2019
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, [] {});
> +}
>
More information about the Gcc-patches
mailing list