diff mbox series

C++ PATCH for c++/89612 - ICE with member friend template with noexcept

Message ID 20190307215255.GI26967@redhat.com
State New
Headers show
Series C++ PATCH for c++/89612 - ICE with member friend template with noexcept | expand

Commit Message

Marek Polacek March 7, 2019, 9:52 p.m. UTC
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.

Comments

Marek Polacek March 14, 2019, 1:06 p.m. UTC | #1
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
Jason Merrill March 14, 2019, 8:22 p.m. UTC | #2
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
Marek Polacek March 19, 2019, 3:45 p.m. UTC | #3
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
Jason Merrill March 21, 2019, 8 p.m. UTC | #4
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
Marek Polacek March 28, 2019, 6:59 p.m. UTC | #5
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, [] {});
+}
Jason Merrill March 28, 2019, 8:04 p.m. UTC | #6
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 mbox series

Patch

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, [] {});
+}