Re: [PATCH] c++: ICE with late parsing of noexcept in nested class [PR98899]

2021-02-03 Thread Jason Merrill via Gcc-patches

On 2/2/21 5:09 PM, Marek Polacek wrote:

Here we crash with a noexcept-specifier in a nested template class,
because my handling of such deferred-parse noexcept-specifiers was
gronked when we need to instantiate a DEFERRED_PARSE before it was
actually parsed at the end of the outermost class.

In

   struct S {
 template struct B {
   B() noexcept(noexcept(x));
   int x;
 };
 struct A : B {
   A() : B() {}
 };
   };

we call complete_type for B which triggers tsubsting S::B::B()
whose noexcept-specifier still contains a DEFERRED_PARSE.  The trick is
to stash such noexcept-specifiers into DEFPARSE_INSTANTIATIONS so that
we can replace it later when we've finally parsed all deferred
noexcept-specifiers.

In passing, fix missing usage of UNPARSED_NOEXCEPT_SPEC_P.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10?


OK.


gcc/cp/ChangeLog:

PR c++/98899
* parser.c (cp_parser_class_specifier_1): Use any possible
DEFPARSE_INSTANTIATIONS to update DEFERRED_NOEXCEPT_PATTERN.
(cp_parser_save_noexcept): Initialize DEFPARSE_INSTANTIATIONS.
* pt.c (tsubst_exception_specification): Stash new_specs into
DEFPARSE_INSTANTIATIONS.
* tree.c (fixup_deferred_exception_variants): Use
UNPARSED_NOEXCEPT_SPEC_P.

gcc/testsuite/ChangeLog:

PR c++/98899
* g++.dg/cpp0x/noexcept65.C: New test.
---
  gcc/cp/parser.c | 13 ++---
  gcc/cp/pt.c | 16 +++
  gcc/cp/tree.c   |  3 +--
  gcc/testsuite/g++.dg/cpp0x/noexcept65.C | 35 +
  4 files changed, 62 insertions(+), 5 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept65.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index abadaf972d6..5da8670f0e2 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -25026,8 +25026,8 @@ cp_parser_class_specifier_1 (cp_parser* parser)
  pushed_scope = push_scope (class_type);
}
  
-	  tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));

- spec = TREE_PURPOSE (spec);
+ tree def_parse = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ def_parse = TREE_PURPOSE (def_parse);
  
  	  /* Make sure that any template parameters are in scope.  */

  maybe_begin_member_template_processing (decl);
@@ -25044,7 +25044,7 @@ cp_parser_class_specifier_1 (cp_parser* parser)
parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
  
  	  /* Now we can parse the noexcept-specifier.  */

- spec = cp_parser_late_noexcept_specifier (parser, spec);
+ tree spec = cp_parser_late_noexcept_specifier (parser, def_parse);
  
  	  if (spec == error_mark_node)

spec = NULL_TREE;
@@ -25052,6 +25052,12 @@ cp_parser_class_specifier_1 (cp_parser* parser)
  /* Update the fn's type directly -- it might have escaped
 beyond this decl :(  */
  fixup_deferred_exception_variants (TREE_TYPE (decl), spec);
+ /* Update any instantiations we've already created.  We must
+keep the new noexcept-specifier wrapped in a DEFERRED_NOEXCEPT
+so that maybe_instantiate_noexcept can tsubst the NOEXCEPT_EXPR
+in the pattern.  */
+ for (tree i : DEFPARSE_INSTANTIATIONS (def_parse))
+   DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (i)) = TREE_PURPOSE (spec);
  
  	  /* Restore the state of local_variables_forbidden_p.  */

  parser->local_variables_forbidden_p = local_variables_forbidden_p;
@@ -26695,6 +26701,7 @@ cp_parser_save_noexcept (cp_parser *parser)
/* Save away the noexcept-specifier; we will process it when the
   class is complete.  */
DEFPARSE_TOKENS (expr) = cp_token_cache_new (first, last);
+  DEFPARSE_INSTANTIATIONS (expr) = nullptr;
expr = build_tree_list (expr, NULL_TREE);
return expr;
  }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index aa1687a9f2a..4781519d00f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15189,6 +15189,22 @@ tsubst_exception_specification (tree fntype,
 /*integral_constant_expression_p=*/true);
}
new_specs = build_noexcept_spec (new_specs, complain);
+  /* We've instantiated a template before a noexcept-specifier
+contained therein has been parsed.  This can happen for
+a nested template class:
+
+ struct S {
+   template struct B { B() noexcept(...); };
+   struct A : B { ... use B() ... };
+ };
+
+where completing B will trigger instantiating the
+noexcept, even though we only parse it at the end of S.  */
+  if (UNPARSED_NOEXCEPT_SPEC_P (specs))
+   {
+ gcc_checking_assert (defer_ok);
+ vec_safe_push (DEFPARSE_INSTANTIATIONS (expr), new_specs);
+   }
  }
else if (specs)
  {
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2e5a1f198e8..e6ced274959 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c

[PATCH] c++: ICE with late parsing of noexcept in nested class [PR98899]

2021-02-02 Thread Marek Polacek via Gcc-patches
Here we crash with a noexcept-specifier in a nested template class,
because my handling of such deferred-parse noexcept-specifiers was
gronked when we need to instantiate a DEFERRED_PARSE before it was
actually parsed at the end of the outermost class.

In

  struct S {
template struct B {
  B() noexcept(noexcept(x));
  int x;
};
struct A : B {
  A() : B() {}
};
  };

we call complete_type for B which triggers tsubsting S::B::B()
whose noexcept-specifier still contains a DEFERRED_PARSE.  The trick is
to stash such noexcept-specifiers into DEFPARSE_INSTANTIATIONS so that
we can replace it later when we've finally parsed all deferred
noexcept-specifiers.

In passing, fix missing usage of UNPARSED_NOEXCEPT_SPEC_P.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10?

gcc/cp/ChangeLog:

PR c++/98899
* parser.c (cp_parser_class_specifier_1): Use any possible
DEFPARSE_INSTANTIATIONS to update DEFERRED_NOEXCEPT_PATTERN.
(cp_parser_save_noexcept): Initialize DEFPARSE_INSTANTIATIONS.
* pt.c (tsubst_exception_specification): Stash new_specs into
DEFPARSE_INSTANTIATIONS.
* tree.c (fixup_deferred_exception_variants): Use
UNPARSED_NOEXCEPT_SPEC_P.

gcc/testsuite/ChangeLog:

PR c++/98899
* g++.dg/cpp0x/noexcept65.C: New test.
---
 gcc/cp/parser.c | 13 ++---
 gcc/cp/pt.c | 16 +++
 gcc/cp/tree.c   |  3 +--
 gcc/testsuite/g++.dg/cpp0x/noexcept65.C | 35 +
 4 files changed, 62 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept65.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index abadaf972d6..5da8670f0e2 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -25026,8 +25026,8 @@ cp_parser_class_specifier_1 (cp_parser* parser)
  pushed_scope = push_scope (class_type);
}
 
- tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
- spec = TREE_PURPOSE (spec);
+ tree def_parse = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ def_parse = TREE_PURPOSE (def_parse);
 
  /* Make sure that any template parameters are in scope.  */
  maybe_begin_member_template_processing (decl);
@@ -25044,7 +25044,7 @@ cp_parser_class_specifier_1 (cp_parser* parser)
parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
 
  /* Now we can parse the noexcept-specifier.  */
- spec = cp_parser_late_noexcept_specifier (parser, spec);
+ tree spec = cp_parser_late_noexcept_specifier (parser, def_parse);
 
  if (spec == error_mark_node)
spec = NULL_TREE;
@@ -25052,6 +25052,12 @@ cp_parser_class_specifier_1 (cp_parser* parser)
  /* Update the fn's type directly -- it might have escaped
 beyond this decl :(  */
  fixup_deferred_exception_variants (TREE_TYPE (decl), spec);
+ /* Update any instantiations we've already created.  We must
+keep the new noexcept-specifier wrapped in a DEFERRED_NOEXCEPT
+so that maybe_instantiate_noexcept can tsubst the NOEXCEPT_EXPR
+in the pattern.  */
+ for (tree i : DEFPARSE_INSTANTIATIONS (def_parse))
+   DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (i)) = TREE_PURPOSE (spec);
 
  /* Restore the state of local_variables_forbidden_p.  */
  parser->local_variables_forbidden_p = local_variables_forbidden_p;
@@ -26695,6 +26701,7 @@ cp_parser_save_noexcept (cp_parser *parser)
   /* Save away the noexcept-specifier; we will process it when the
  class is complete.  */
   DEFPARSE_TOKENS (expr) = cp_token_cache_new (first, last);
+  DEFPARSE_INSTANTIATIONS (expr) = nullptr;
   expr = build_tree_list (expr, NULL_TREE);
   return expr;
 }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index aa1687a9f2a..4781519d00f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15189,6 +15189,22 @@ tsubst_exception_specification (tree fntype,
 /*integral_constant_expression_p=*/true);
}
   new_specs = build_noexcept_spec (new_specs, complain);
+  /* We've instantiated a template before a noexcept-specifier
+contained therein has been parsed.  This can happen for
+a nested template class:
+
+ struct S {
+   template struct B { B() noexcept(...); };
+   struct A : B { ... use B() ... };
+ };
+
+where completing B will trigger instantiating the
+noexcept, even though we only parse it at the end of S.  */
+  if (UNPARSED_NOEXCEPT_SPEC_P (specs))
+   {
+ gcc_checking_assert (defer_ok);
+ vec_safe_push (DEFPARSE_INSTANTIATIONS (expr), new_specs);
+   }
 }
   else if (specs)
 {
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2e5a1f198e8..e6ced274959 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2738,8 +2738,7 @@ fixup_deferred_exception_variants