PR c++/86476 - noexcept-specifier is a complete-class context.
authorMarek Polacek <polacek@redhat.com>
Sat, 22 Jun 2019 15:14:30 +0000 (15:14 +0000)
committerMarek Polacek <mpolacek@gcc.gnu.org>
Sat, 22 Jun 2019 15:14:30 +0000 (15:14 +0000)
PR c++/52869
* cp-tree.def (DEFAULT_ARG): Update commentary.
* cp-tree.h (UNPARSED_NOEXCEPT_SPEC_P): New macro.
(tree_default_arg): Use tree_base instead of tree_common.
(do_push_parm_decls, maybe_check_overriding_exception_spec): Declare.
* decl.c (do_push_parm_decls): New function, broken out of...
(store_parm_decls): ...here.  Call it.
* except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
* parser.c (cp_parser_noexcept_specification_opt,
cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
Forward-declare.
(unparsed_noexcepts): New macro.
(push_unparsed_function_queues): Update initializer.
(cp_parser_direct_declarator): Pass FRIEND_P to
cp_parser_exception_specification_opt.
(inject_parm_decls): New.
(pop_injected_parms): New.
(cp_parser_class_specifier_1): Implement delayed parsing of
noexcept-specifiers.
(cp_parser_save_noexcept): New.
(cp_parser_late_noexcept_specifier): New.
(noexcept_override_late_checks): New.
(cp_parser_noexcept_specification_opt): Add FRIEND_P parameter.  Call
cp_parser_save_noexcept instead of the normal processing if needed.
(cp_parser_exception_specification_opt): Add FRIEND_P parameter and
pass it to cp_parser_noexcept_specification_opt.
(cp_parser_save_member_function_body): Fix comment.
(cp_parser_save_default_args): Maybe save the noexcept-specifier to
post process.
(cp_parser_transaction): Update call to
cp_parser_noexcept_specification_opt.
(cp_parser_transaction_expression): Likewise.
* parser.h (cp_unparsed_functions_entry): Add new field to carry
a noexcept-specifier.
* pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
* search.c (maybe_check_overriding_exception_spec): New function, broken
out of...
(check_final_overrider): ...here.  Call
maybe_check_overriding_exception_spec.
* tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
(cp_tree_equal): Handle DEFAULT_ARG.

* g++.dg/cpp0x/noexcept45.C: New test.
* g++.dg/cpp0x/noexcept46.C: New test.
* g++.dg/cpp0x/noexcept47.C: New test.
* g++.dg/cpp0x/noexcept48.C: New test.
* g++.dg/cpp0x/noexcept49.C: New test.
* g++.dg/cpp0x/noexcept50.C: New test.
* g++.dg/cpp0x/noexcept51.C: New test.
* g++.dg/cpp0x/noexcept52.C: New test.
* g++.dg/cpp0x/noexcept53.C: New test.
* g++.dg/eh/shadow1.C: Adjust dg-error.

From-SVN: r272586

21 files changed:
gcc/cp/ChangeLog
gcc/cp/cp-tree.def
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/except.c
gcc/cp/parser.c
gcc/cp/parser.h
gcc/cp/pt.c
gcc/cp/search.c
gcc/cp/tree.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/noexcept45.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/noexcept46.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/noexcept47.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/noexcept48.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/noexcept49.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/noexcept50.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/noexcept51.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/noexcept52.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/noexcept53.C [new file with mode: 0644]
gcc/testsuite/g++.dg/eh/shadow1.C

index 31336f954c34e5c1be24006d56eb68453de127f5..800ae29a380594ecd9173717d5c346f9a3c99f60 100644 (file)
@@ -1,5 +1,48 @@
 2019-06-22  Marek Polacek  <polacek@redhat.com>
 
+       PR c++/86476 - noexcept-specifier is a complete-class context.
+       PR c++/52869
+       * cp-tree.def (DEFAULT_ARG): Update commentary.
+       * cp-tree.h (UNPARSED_NOEXCEPT_SPEC_P): New macro.
+       (tree_default_arg): Use tree_base instead of tree_common.
+       (do_push_parm_decls, maybe_check_overriding_exception_spec): Declare.
+       * decl.c (do_push_parm_decls): New function, broken out of...
+       (store_parm_decls): ...here.  Call it.
+       * except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
+       * parser.c (cp_parser_noexcept_specification_opt,
+       cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
+       Forward-declare.
+       (unparsed_noexcepts): New macro.
+       (push_unparsed_function_queues): Update initializer.
+       (cp_parser_direct_declarator): Pass FRIEND_P to
+       cp_parser_exception_specification_opt.
+       (inject_parm_decls): New.
+       (pop_injected_parms): New.
+       (cp_parser_class_specifier_1): Implement delayed parsing of
+       noexcept-specifiers.
+       (cp_parser_save_noexcept): New.
+       (cp_parser_late_noexcept_specifier): New.
+       (noexcept_override_late_checks): New.
+       (cp_parser_noexcept_specification_opt): Add FRIEND_P parameter.  Call
+       cp_parser_save_noexcept instead of the normal processing if needed.
+       (cp_parser_exception_specification_opt): Add FRIEND_P parameter and
+       pass it to cp_parser_noexcept_specification_opt.
+       (cp_parser_save_member_function_body): Fix comment.
+       (cp_parser_save_default_args): Maybe save the noexcept-specifier to
+       post process.
+       (cp_parser_transaction): Update call to
+       cp_parser_noexcept_specification_opt.
+       (cp_parser_transaction_expression): Likewise.
+       * parser.h (cp_unparsed_functions_entry): Add new field to carry
+       a noexcept-specifier.
+       * pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
+       * search.c (maybe_check_overriding_exception_spec): New function, broken
+       out of...
+       (check_final_overrider): ...here.  Call
+       maybe_check_overriding_exception_spec.
+       * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
+       (cp_tree_equal): Handle DEFAULT_ARG.
+
        PR c++/90881 - bogus -Wunused-value in unevaluated context.
        * cvt.c (convert_to_void): Don't emit unused warnings in
        an unevaluated context.
index 03c105b5c4c7393db4d056d5eaf13d074edfca7b..475c584fd4cd6dd4c868733cd215623e11aad85d 100644 (file)
@@ -209,7 +209,8 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
 
 /* An un-parsed default argument.  Holds a vector of input tokens and
    a vector of places where the argument was instantiated before
-   parsing had occurred.  */
+   parsing had occurred.  This is also used for delayed NSDMIs and
+   noexcept-specifier parsing.  */
 DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
 
 /* An uninstantiated/unevaluated noexcept-specification.  For the
index 98f7a0c0cd00e27d80272632ebc85f6ecea298d8..2c05e638915c48f84594ae4fb797318c70a1cd15 100644 (file)
@@ -1190,7 +1190,7 @@ enum cp_identifier_kind {
   (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
 
 struct GTY (()) tree_default_arg {
-  struct tree_common common;
+  struct tree_base base;
   struct cp_token_cache *tokens;
   vec<tree, va_gc> *instantiations;
 };
@@ -1206,6 +1206,9 @@ struct GTY (()) tree_default_arg {
 #define UNEVALUATED_NOEXCEPT_SPEC_P(NODE)                              \
   (DEFERRED_NOEXCEPT_SPEC_P (NODE)                                     \
    && DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
+#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
+  ((NODE) && (TREE_PURPOSE (NODE)) \
+   && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
 
 struct GTY (()) tree_deferred_noexcept {
   struct tree_base base;
@@ -6467,6 +6470,7 @@ extern bool check_array_designated_initializer  (constructor_elt *,
                                                 unsigned HOST_WIDE_INT);
 extern bool check_for_uninitialized_const_var   (tree, bool, tsubst_flags_t);
 extern tree build_explicit_specifier           (tree, tsubst_flags_t);
+extern void do_push_parm_decls                 (tree, tree, tree *);
 
 /* in decl2.c */
 extern void record_mangling                    (tree, bool);
@@ -6932,6 +6936,7 @@ extern tree copied_binfo                  (tree, tree);
 extern tree original_binfo                     (tree, tree);
 extern int shared_member_p                     (tree);
 extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
+extern bool maybe_check_overriding_exception_spec (tree, tree);
 
 /* The representation of a deferred access check.  */
 
index bcebdc9c0f525ffddfaa6f9655e13c8cca11f6d7..8a82c754ef77eab8757e488d10c6cfee85e40e31 100644 (file)
@@ -15761,6 +15761,39 @@ use_eh_spec_block (tree fn)
          && !DECL_DEFAULTED_FN (fn));
 }
 
+/* Helper function to push ARGS into the current lexical scope.  DECL
+   is the function declaration.  NONPARMS is used to handle enum
+   constants.  */
+
+void
+do_push_parm_decls (tree decl, tree args, tree *nonparms)
+{
+  /* If we're doing semantic analysis, then we'll call pushdecl
+     for each of these.  We must do them in reverse order so that
+     they end in the correct forward order.  */
+  args = nreverse (args);
+
+  tree next;
+  for (tree parm = args; parm; parm = next)
+    {
+      next = DECL_CHAIN (parm);
+      if (TREE_CODE (parm) == PARM_DECL)
+       pushdecl (parm);
+      else if (nonparms)
+       {
+         /* If we find an enum constant or a type tag, put it aside for
+            the moment.  */
+         TREE_CHAIN (parm) = NULL_TREE;
+         *nonparms = chainon (*nonparms, parm);
+       }
+    }
+
+  /* Get the decls in their original chain order and record in the
+     function.  This is all and only the PARM_DECLs that were
+     pushed into scope by the loop above.  */
+  DECL_ARGUMENTS (decl) = get_local_decls ();
+}
+
 /* Store the parameter declarations into the current function declaration.
    This is called after parsing the parameter declarations, before
    digesting the body of the function.
@@ -15771,7 +15804,6 @@ static void
 store_parm_decls (tree current_function_parms)
 {
   tree fndecl = current_function_decl;
-  tree parm;
 
   /* This is a chain of any other decls that came in among the parm
      declarations.  If a parm is declared with  enum {foo, bar} x;
@@ -15786,35 +15818,12 @@ store_parm_decls (tree current_function_parms)
         and complain if any redundant old-style parm decls were written.  */
 
       tree specparms = current_function_parms;
-      tree next;
 
       /* Must clear this because it might contain TYPE_DECLs declared
             at class level.  */
       current_binding_level->names = NULL;
 
-      /* If we're doing semantic analysis, then we'll call pushdecl
-            for each of these.  We must do them in reverse order so that
-            they end in the correct forward order.  */
-      specparms = nreverse (specparms);
-
-      for (parm = specparms; parm; parm = next)
-       {
-         next = DECL_CHAIN (parm);
-         if (TREE_CODE (parm) == PARM_DECL)
-           pushdecl (parm);
-         else
-           {
-             /* If we find an enum constant or a type tag,
-                put it aside for the moment.  */
-             TREE_CHAIN (parm) = NULL_TREE;
-             nonparms = chainon (nonparms, parm);
-           }
-       }
-
-      /* Get the decls in their original chain order and record in the
-        function.  This is all and only the PARM_DECLs that were
-        pushed into scope by the loop above.  */
-      DECL_ARGUMENTS (fndecl) = get_local_decls ();
+      do_push_parm_decls (fndecl, specparms, &nonparms);
     }
   else
     DECL_ARGUMENTS (fndecl) = NULL_TREE;
index 71f5d609f1006890832e8d22a82489725225c449..1f87c5ab695b9e0e7a71724e712427aa119b8c5b 100644 (file)
@@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
              || TREE_VALUE (spec)
              || spec == noexcept_false_spec
              || TREE_PURPOSE (spec) == error_mark_node
+             || UNPARSED_NOEXCEPT_SPEC_P (spec)
              || processing_template_decl);
 
   return false;
index 5cbc4551d1a335c767abc96492d921db18c97210..4d4d32973d937e18f2f0ff23d3c3950229f9cee0 100644 (file)
@@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
 
 static cp_token_cache *cp_token_cache_new
   (cp_token *, cp_token *);
+static tree cp_parser_noexcept_specification_opt
+  (cp_parser *, bool, bool *, bool, bool);
+static tree cp_parser_late_noexcept_specifier
+  (cp_parser *, tree);
+static void noexcept_override_late_checks
+  (tree, tree);
 
 static void cp_parser_initial_pragma
   (cp_token *);
@@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next)
   parser->unparsed_queues->last ().nsdmis
 #define unparsed_classes \
   parser->unparsed_queues->last ().classes
+#define unparsed_noexcepts \
+  parser->unparsed_queues->last ().noexcepts
 
 static void
 push_unparsed_function_queues (cp_parser *parser)
 {
-  cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
+  cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
+                                   NULL };
   vec_safe_push (parser->unparsed_queues, e);
 }
 
@@ -2361,7 +2370,7 @@ static tree cp_parser_exception_declaration
 static tree cp_parser_throw_expression
   (cp_parser *);
 static tree cp_parser_exception_specification_opt
-  (cp_parser *);
+  (cp_parser *, bool = false);
 static tree cp_parser_type_id_list
   (cp_parser *);
 
@@ -20816,7 +20825,7 @@ cp_parser_direct_declarator (cp_parser* parser,
                  tree tx_qual = cp_parser_tx_qualifier_opt (parser);
                  /* And the exception-specification.  */
                  exception_specification
-                   = cp_parser_exception_specification_opt (parser);
+                   = cp_parser_exception_specification_opt (parser, friend_p);
 
                  attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -23310,6 +23319,34 @@ cp_parser_class_name (cp_parser *parser,
   return decl;
 }
 
+/* Make sure that any member-function parameters are in scope.
+   For instance, a function's noexcept-specifier can use the function's
+   parameters:
+
+   struct S {
+     void fn (int p) noexcept(noexcept(p));
+   };
+
+   so we need to make sure name lookup can find them.  This is used
+   when we delay parsing of the noexcept-specifier.  */
+
+static void
+inject_parm_decls (tree decl)
+{
+  begin_scope (sk_function_parms, decl);
+  tree args = DECL_ARGUMENTS (decl);
+
+  do_push_parm_decls (decl, args, /*nonparms=*/NULL);
+}
+
+/* Undo the effects of inject_parm_decls.  */
+
+static void
+pop_injected_parms (void)
+{
+  pop_bindings_and_leave_scope ();
+}
+
 /* Parse a class-specifier.
 
    class-specifier:
@@ -23620,6 +23657,60 @@ cp_parser_class_specifier_1 (cp_parser* parser)
       vec_safe_truncate (unparsed_classes, 0);
       after_nsdmi_defaulted_late_checks (type);
 
+      /* If there are noexcept-specifiers that have not yet been processed,
+        take care of them now.  */
+      class_type = NULL_TREE;
+      pushed_scope = NULL_TREE;
+      FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
+       {
+         tree ctx = DECL_CONTEXT (decl);
+         if (class_type != ctx)
+           {
+             if (pushed_scope)
+               pop_scope (pushed_scope);
+             class_type = ctx;
+             pushed_scope = push_scope (class_type);
+           }
+
+         tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+         spec = TREE_PURPOSE (spec);
+
+         /* Make sure that any template parameters are in scope.  */
+         maybe_begin_member_template_processing (decl);
+
+         /* Make sure that any member-function parameters are in scope.  */
+         inject_parm_decls (decl);
+
+         /* 'this' is not allowed in static member functions.  */
+         unsigned char local_variables_forbidden_p
+           = parser->local_variables_forbidden_p;
+         if (DECL_THIS_STATIC (decl))
+           parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
+
+         /* Now we can parse the noexcept-specifier.  */
+         spec = cp_parser_late_noexcept_specifier (parser, spec);
+
+         if (spec != error_mark_node)
+           TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
+
+         /* Restore the state of local_variables_forbidden_p.  */
+         parser->local_variables_forbidden_p = local_variables_forbidden_p;
+
+         /* The finish_struct call above performed various override checking,
+            but it skipped unparsed noexcept-specifier operands.  Now that we
+            have resolved them, check again.  */
+         noexcept_override_late_checks (type, decl);
+
+         /* Remove any member-function parameters from the symbol table.  */
+         pop_injected_parms ();
+
+         /* Remove any template parameters from the symbol table.  */
+         maybe_end_member_template_processing ();
+       }
+      vec_safe_truncate (unparsed_noexcepts, 0);
+      if (pushed_scope)
+       pop_scope (pushed_scope);
+
       /* Now parse the body of the functions.  */
       if (flag_openmp)
        {
@@ -25175,6 +25266,89 @@ cp_parser_base_specifier (cp_parser* parser)
 
 /* Exception handling [gram.exception] */
 
+/* Save the tokens that make up the noexcept-specifier for a member-function.
+   Returns a DEFAULT_ARG.  */
+
+static tree
+cp_parser_save_noexcept (cp_parser *parser)
+{
+  cp_token *first = parser->lexer->next_token;
+  /* We want everything up to, including, the final ')'.  */
+  cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
+  cp_token *last = parser->lexer->next_token;
+
+  /* As with default arguments and NSDMIs, make use of DEFAULT_ARG
+     to carry the information we will need.  */
+  tree expr = make_node (DEFAULT_ARG);
+  /* Save away the noexcept-specifier; we will process it when the
+     class is complete.  */
+  DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
+  expr = build_tree_list (expr, NULL_TREE);
+  return expr;
+}
+
+/* Used for late processing of noexcept-specifiers of member-functions.
+   DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
+   we saved for later; parse it now.  */
+
+static tree
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+{
+  /* Make sure we've gotten something that hasn't been parsed yet.  */
+  gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
+
+  push_unparsed_function_queues (parser);
+
+  /* Push the saved tokens for the noexcept-specifier onto the parser's
+     lexer stack.  */
+  cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
+  cp_parser_push_lexer_for_tokens (parser, tokens);
+
+  /* Parse the cached noexcept-specifier.  */
+  tree parsed_arg
+    = cp_parser_noexcept_specification_opt (parser,
+                                           /*require_constexpr=*/true,
+                                           /*consumed_expr=*/NULL,
+                                           /*return_cond=*/false,
+                                           /*friend_p=*/false);
+
+  /* Revert to the main lexer.  */
+  cp_parser_pop_lexer (parser);
+
+  /* Restore the queue.  */
+  pop_unparsed_function_queues (parser);
+
+  /* And we're done.  */
+  return parsed_arg;
+}
+
+/* Perform late checking of overriding function with respect to their
+   noexcept-specifiers.  TYPE is the class and FNDECL is the function
+   that potentially overrides some virtual function with the same
+   signature.  */
+
+static void
+noexcept_override_late_checks (tree type, tree fndecl)
+{
+  tree binfo = TYPE_BINFO (type);
+  tree base_binfo;
+
+  if (DECL_STATIC_FUNCTION_P (fndecl))
+    return;
+
+  for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+    {
+      tree basetype = BINFO_TYPE (base_binfo);
+
+      if (!TYPE_POLYMORPHIC_P (basetype))
+       continue;
+
+      tree fn = look_for_overrides_here (basetype, fndecl);
+      if (fn)
+       maybe_check_overriding_exception_spec (fndecl, fn);
+    }
+}
+
 /* Parse an (optional) noexcept-specification.
 
    noexcept-specification:
@@ -25185,13 +25359,15 @@ cp_parser_base_specifier (cp_parser* parser)
    expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if
    there are no parentheses.  CONSUMED_EXPR will be set accordingly.
    Otherwise, returns a noexcept specification unless RETURN_COND is true,
-   in which case a boolean condition is returned instead.  */
+   in which case a boolean condition is returned instead.  If FRIEND_P is true,
+   the function with this noexcept-specification had the `friend' specifier.  */
 
 static tree
 cp_parser_noexcept_specification_opt (cp_parser* parser,
                                      bool require_constexpr,
                                      bool* consumed_expr,
-                                     bool return_cond)
+                                     bool return_cond,
+                                     bool friend_p)
 {
   cp_token *token;
   const char *saved_message;
@@ -25203,6 +25379,27 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
   if (cp_parser_is_keyword (token, RID_NOEXCEPT))
     {
       tree expr;
+
+      /* [class.mem]/6 says that a noexcept-specifer (within the
+        member-specification of the class) is a complete-class context of
+        a class.  So, if the noexcept-specifier has the optional expression,
+        just save the tokens, and reparse this after we're done with the
+        class.  */
+      const bool literal_p
+       = ((cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
+           || cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD))
+          && cp_lexer_nth_token_is (parser->lexer, 4, CPP_CLOSE_PAREN));
+
+      if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
+         /* No need to delay parsing for a number literal or true/false.  */
+         && !literal_p
+         && at_class_scope_p ()
+         /* Don't delay parsing for friend member functions.  */
+         && !friend_p
+         && TYPE_BEING_DEFINED (current_class_type)
+         && !LAMBDA_TYPE_P (current_class_type))
+       return cp_parser_save_noexcept (parser);
+
       cp_lexer_consume_token (parser->lexer);
 
       if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
@@ -25273,10 +25470,11 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
      throw ( type-id-list [opt] )
 
    Returns a TREE_LIST representing the exception-specification.  The
-   TREE_VALUE of each node is a type.  */
+   TREE_VALUE of each node is a type.  If FRIEND_P is true, the function
+   with this noexcept-specification had the `friend' specifier.  */
 
 static tree
-cp_parser_exception_specification_opt (cp_parser* parser)
+cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
 {
   cp_token *token;
   tree type_id_list;
@@ -25286,8 +25484,12 @@ cp_parser_exception_specification_opt (cp_parser* parser)
   token = cp_lexer_peek_token (parser->lexer);
 
   /* Is it a noexcept-specification?  */
-  type_id_list = cp_parser_noexcept_specification_opt (parser, true, NULL,
-                                                      false);
+  type_id_list
+    = cp_parser_noexcept_specification_opt (parser,
+                                           /*require_constexpr=*/true,
+                                           /*consumed_expr=*/NULL,
+                                           /*return_cond=*/false,
+                                           friend_p);
   if (type_id_list != NULL_TREE)
     return type_id_list;
 
@@ -28435,7 +28637,7 @@ cp_parser_save_member_function_body (cp_parser* parser,
       return error_mark_node;
     }
 
-  /* Remember it, if there default args to post process.  */
+  /* Remember it, if there are default args to post process.  */
   cp_parser_save_default_args (parser, fn);
 
   /* Save away the tokens that make up the body of the
@@ -28728,6 +28930,11 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
        vec_safe_push (unparsed_funs_with_default_args, entry);
        break;
       }
+
+  /* Remember if there is a noexcept-specifier to post process.  */
+  tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+  if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+    vec_safe_push (unparsed_noexcepts, decl);
 }
 
 /* DEFAULT_ARG contains the saved tokens for the initializer of DECL,
@@ -40599,7 +40806,11 @@ cp_parser_transaction (cp_parser *parser, cp_token *token)
       noex = NULL_TREE;
     }
   else
-    noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true);
+    noex = cp_parser_noexcept_specification_opt (parser,
+                                                /*require_constexpr=*/true,
+                                                /*consumed_expr=*/NULL,
+                                                /*return_cond=*/true,
+                                                /*friend_p=*/false);
 
   /* Keep track if we're in the lexical scope of an outer transaction.  */
   new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -40659,8 +40870,11 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
   parser->in_transaction = this_in;
 
   /* Parse a noexcept specification.  */
-  noex = cp_parser_noexcept_specification_opt (parser, false, &noex_expr,
-                                              true);
+  noex = cp_parser_noexcept_specification_opt (parser,
+                                              /*require_constexpr=*/false,
+                                              &noex_expr,
+                                              /*return_cond=*/true,
+                                              /*friend_p=*/false);
 
   if (!noex || !noex_expr
       || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
index c03a9d87af591c91b3b289e614605faf5f7c5673..2890788f4899acfb5ef68eeb5915f0f7cd77fd67 100644 (file)
@@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
   /* Nested classes go in this vector, so that we can do some final
      processing after parsing any NSDMIs.  */
   vec<tree, va_gc> *classes;
+
+  /* Functions with noexcept-specifiers that require post-processing.  */
+  vec<tree, va_gc> *noexcepts;
 };
 
 
index 69de55369dd081bfc48eb6c1f0c648b22e1f9415..fb89b933524b98744a89f059725f3b6e500054fc 100644 (file)
@@ -25313,8 +25313,9 @@ dependent_type_p_r (tree type)
          if (tree noex = TREE_PURPOSE (spec))
            /* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
               affect overload resolution and treating it as dependent breaks
-              things.  */
+              things.  Same for an unparsed noexcept expression.  */
            if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
+               && TREE_CODE (noex) != DEFAULT_ARG
                && value_dependent_expression_p (noex))
              return true;
       return false;
index dac08d44d764c01d5d582a815884fc8f36e9d554..372c4424747b9bc28f40ecc0a2bf5979a9109dd8 100644 (file)
@@ -1860,6 +1860,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
                                   NULL, &lfd);
 }
 
+/* Check throw specifier of OVERRIDER is at least as strict as
+   the one of BASEFN.  */
+
+bool
+maybe_check_overriding_exception_spec (tree overrider, tree basefn)
+{
+  maybe_instantiate_noexcept (basefn);
+  maybe_instantiate_noexcept (overrider);
+  tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
+  tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
+
+  if (DECL_INVALID_OVERRIDER_P (overrider))
+    return true;
+
+  /* Can't check this yet.  Pretend this is fine and let
+     noexcept_override_late_checks check this later.  */
+  if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
+      || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
+    return true;
+
+  if (!comp_except_specs (base_throw, over_throw, ce_derived))
+    {
+      auto_diagnostic_group d;
+      error ("looser exception specification on overriding virtual function "
+            "%q+#F", overrider);
+      inform (DECL_SOURCE_LOCATION (basefn),
+             "overridden function is %q#F", basefn);
+      DECL_INVALID_OVERRIDER_P (overrider) = 1;
+      return false;
+    }
+  return true;
+}
+
 /* Check that virtual overrider OVERRIDER is acceptable for base function
    BASEFN. Issue diagnostic, and return zero, if unacceptable.  */
 
@@ -1870,7 +1903,6 @@ check_final_overrider (tree overrider, tree basefn)
   tree base_type = TREE_TYPE (basefn);
   tree over_return = fndecl_declared_return_type (overrider);
   tree base_return = fndecl_declared_return_type (basefn);
-  tree over_throw, base_throw;
 
   int fail = 0;
 
@@ -1954,21 +1986,8 @@ check_final_overrider (tree overrider, tree basefn)
       return 0;
     }
 
-  /* Check throw specifier is at least as strict.  */
-  maybe_instantiate_noexcept (basefn);
-  maybe_instantiate_noexcept (overrider);
-  base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
-  over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
-
-  if (!comp_except_specs (base_throw, over_throw, ce_derived))
-    {
-      auto_diagnostic_group d;
-      error ("looser throw specifier for %q+#F", overrider);
-      inform (DECL_SOURCE_LOCATION (basefn),
-             "overridden function is %q#F", basefn);
-      DECL_INVALID_OVERRIDER_P (overrider) = 1;
-      return 0;
-    }
+  if (!maybe_check_overriding_exception_spec (overrider, basefn))
+    return 0;
 
   /* Check for conflicting type attributes.  But leave transaction_safe for
      set_one_vmethod_tm_attributes.  */
index 978aea56193592fb3a92bc77068c7bb434aadc4a..ebfe362595ff8bf412012646f41470a04cb57d9a 100644 (file)
@@ -2546,6 +2546,7 @@ canonical_eh_spec (tree raises)
   if (raises == NULL_TREE)
     return raises;
   else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
+          || UNPARSED_NOEXCEPT_SPEC_P (raises)
           || uses_template_parms (raises)
           || uses_template_parms (TREE_PURPOSE (raises)))
     /* Keep a dependent or deferred exception specification.  */
@@ -3656,6 +3657,7 @@ cp_tree_equal (tree t1, tree t2)
     case IDENTIFIER_NODE:
     case SSA_NAME:
     case USING_DECL:
+    case DEFAULT_ARG:
       return false;
 
     case BASELINK:
index b0b1d4ba912201f7a791769255cda7101cec4a51..e38abbf7be3f42bf8d0be04f9c2227a12c1ba9d5 100644 (file)
@@ -1,5 +1,18 @@
 2019-06-22  Marek Polacek  <polacek@redhat.com>
 
+       PR c++/86476 - noexcept-specifier is a complete-class context.
+       PR c++/52869
+       * g++.dg/cpp0x/noexcept45.C: New test.
+       * g++.dg/cpp0x/noexcept46.C: New test.
+       * g++.dg/cpp0x/noexcept47.C: New test.
+       * g++.dg/cpp0x/noexcept48.C: New test.
+       * g++.dg/cpp0x/noexcept49.C: New test.
+       * g++.dg/cpp0x/noexcept50.C: New test.
+       * g++.dg/cpp0x/noexcept51.C: New test.
+       * g++.dg/cpp0x/noexcept52.C: New test.
+       * g++.dg/cpp0x/noexcept53.C: New test.
+       * g++.dg/eh/shadow1.C: Adjust dg-error.
+
        PR c++/90881 - bogus -Wunused-value in unevaluated context.
        * g++.dg/cpp0x/Wunused-value1.C: New test.
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept45.C b/gcc/testsuite/g++.dg/cpp0x/noexcept45.C
new file mode 100644 (file)
index 0000000..39df4a6
--- /dev/null
@@ -0,0 +1,23 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+  virtual void f();
+  virtual void g() noexcept;
+  virtual void h() noexcept(false);
+};
+
+struct B : A
+{
+  void f() noexcept(true);
+  void g() noexcept(true);
+  void h() noexcept(true);
+};
+
+struct D : A
+{
+  void f() noexcept(false);
+  void g() noexcept(false); // { dg-error "looser exception specification" }
+  void h() noexcept(false);
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept46.C b/gcc/testsuite/g++.dg/cpp0x/noexcept46.C
new file mode 100644 (file)
index 0000000..da7490d
--- /dev/null
@@ -0,0 +1,28 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void f() noexcept(false);
+void g() noexcept(true);
+void h() noexcept;
+
+struct B {
+  friend void f() noexcept(false);
+  friend void g() noexcept(false); // { dg-error "different exception specifier" }
+  friend void h() noexcept(false); // { dg-error "different exception specifier" }
+};
+
+struct C {
+  friend void f() noexcept(true); // { dg-error "different exception specifier" }
+  friend void g() noexcept(true); // { dg-error "different exception specifier" }
+  friend void h() noexcept(true); // { dg-error "different exception specifier" }
+};
+
+void o() noexcept(false);
+void p() noexcept(true);
+void q() noexcept;
+
+struct D {
+  friend void o() noexcept(true); // { dg-error "different exception specifier" }
+  friend void p() noexcept(true);
+  friend void q() noexcept(true);
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept47.C b/gcc/testsuite/g++.dg/cpp0x/noexcept47.C
new file mode 100644 (file)
index 0000000..0848e68
--- /dev/null
@@ -0,0 +1,83 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+int fn1 ();
+int fn2 () noexcept;
+int fn3 () noexcept;
+
+void g() noexcept(noexcept (fn2()));
+
+struct S1 {
+  friend void g1() noexcept(noexcept(fn2()));
+  friend void g1() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+};
+
+struct S2 {
+  friend void g2() noexcept(noexcept(fn1()));
+  friend void g2() noexcept(noexcept(fn1()));
+  friend void g2() noexcept(noexcept(fn1()));
+};
+
+struct S3 {
+  friend void g3() noexcept(noexcept(fn1()));
+  friend void g3() noexcept(noexcept(fn3())); // { dg-error "different exception specifier" }
+};
+
+struct S4 {
+  friend void g4() noexcept(noexcept(fn2()));
+  friend void g4() noexcept(noexcept(fn3()));
+};
+
+struct S5 {
+  friend void g() noexcept(noexcept(fn3()));
+};
+
+struct S6 {
+  friend void g() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+};
+
+struct S7 {
+  friend void gg() noexcept(noexcept(fn3()));
+};
+
+void gg() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+
+struct S8 {
+  friend void g8();
+  friend void g8() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
+};
+
+struct S9 {
+  friend void g9();
+  friend void g9() noexcept(noexcept(fn1()));
+};
+
+struct S10 {
+  friend void g10() noexcept(noexcept(fn1()));
+  friend void g10();
+};
+
+struct S11 {
+  friend void g11() noexcept(noexcept(fn2()));
+  friend void g11(); // { dg-error "different exception specifier" }
+};
+
+struct S12 {
+  friend void g12() noexcept(false);
+  friend void g12() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
+};
+
+struct S13 {
+  friend void g13() noexcept(false);
+  friend void g13() noexcept(noexcept(fn1()));
+};
+
+struct S14 {
+  friend void g14() noexcept(noexcept(fn1()));
+  friend void g14() noexcept(false);
+};
+
+struct S15 {
+  friend void g15() noexcept(noexcept(fn2()));
+  friend void g15() noexcept(false); // { dg-error "different exception specifier" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept48.C b/gcc/testsuite/g++.dg/cpp0x/noexcept48.C
new file mode 100644 (file)
index 0000000..134212c
--- /dev/null
@@ -0,0 +1,11 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+int g;
+
+struct S {
+  int b;
+  friend void fn1(int n) noexcept(noexcept(n));
+  friend void fn2() noexcept(noexcept(g));
+  friend void fn3() noexcept(noexcept(b));
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept49.C b/gcc/testsuite/g++.dg/cpp0x/noexcept49.C
new file mode 100644 (file)
index 0000000..6da7ff3
--- /dev/null
@@ -0,0 +1,12 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+  static void f1() noexcept(b);
+  static constexpr auto b = true;
+};
+
+S s;
+SA(noexcept(s.f1()));
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept50.C b/gcc/testsuite/g++.dg/cpp0x/noexcept50.C
new file mode 100644 (file)
index 0000000..43b38c2
--- /dev/null
@@ -0,0 +1,147 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+  void f1() noexcept(noexcept(i)) { }
+  void f2() noexcept(noexcept(fn())) { }
+  void f3() noexcept(noexcept(fnx())) { }
+  void f4() noexcept(noexcept(i));
+  void f5() noexcept(noexcept(fn()));
+  void f6() noexcept(noexcept(fnx()));
+
+  void f7() noexcept(1);
+  void f8() noexcept(0);
+  void f9() noexcept(b);
+  void f10() noexcept(!b);
+
+  int i;
+  static constexpr auto b = true;
+  void fny() noexcept(noexcept(fn()));
+  void fn();
+  void fnx() noexcept;
+};
+
+S s;
+SA(noexcept(s.f1()));
+SA(!noexcept(s.f2()));
+SA(noexcept(s.f3()));
+SA(noexcept(s.f4()));
+SA(!noexcept(s.f5()));
+SA(noexcept(s.f6()));
+SA(noexcept(s.f7()));
+SA(!noexcept(s.f8()));
+SA(noexcept(s.f9()));
+SA(!noexcept(s.f10()));
+
+struct S2 {
+  struct V {
+    void f1() noexcept(noexcept(fn()));
+    void f2() noexcept(noexcept(fnx()));
+    void f3() noexcept(noexcept(fn())) { }
+    void f4() noexcept(noexcept(fnx())) { }
+    void fn();
+    void fnx() noexcept;
+  } v;
+  void fn();
+  void fnx();
+};
+
+S2 s2;
+SA(!noexcept(s2.v.f1()));
+SA(noexcept(s2.v.f2()));
+SA(!noexcept(s2.v.f3()));
+SA(noexcept(s2.v.f4()));
+
+struct S3 {
+  void f1() noexcept(noexcept(fn()));
+  void f2() noexcept(noexcept(fnx()));
+  void fn();
+  void fnx() noexcept;
+};
+
+void
+S3::f1() noexcept(noexcept(fn()))
+{
+}
+
+void
+S3::f2() noexcept(noexcept(fnx()))
+{
+}
+
+struct S4 {
+  int f1 (int p) noexcept(noexcept(p)) { return p; }
+  int f2 (int p) noexcept(noexcept(p));
+  int f3 (int p = 10) noexcept(noexcept(p));
+  int f4 () noexcept(noexcept(S4{}));
+};
+
+S4 s4;
+SA(noexcept(s4.f1(1)));
+SA(noexcept(s4.f2(1)));
+SA(noexcept(s4.f3()));
+SA(noexcept(s4.f4()));
+
+template<typename T>
+struct S5 {
+  void f1() noexcept(noexcept(i)) { }
+  void f2() noexcept(noexcept(fn())) { }
+  void f3() noexcept(noexcept(fnx())) { }
+  void f4() noexcept(noexcept(i));
+  void f5() noexcept(noexcept(fn()));
+  void f6() noexcept(noexcept(fnx()));
+    
+  int i;
+  void fny() noexcept(noexcept(fn()));
+  void fn();
+  void fnx() noexcept;
+};
+
+S5<int> s5;
+SA(noexcept(s5.f1()));
+SA(!noexcept(s5.f2()));
+SA(noexcept(s5.f3()));
+SA(noexcept(s5.f4()));
+SA(!noexcept(s5.f5()));
+SA(noexcept(s5.f6()));
+
+template<typename T>
+struct S6 {
+  void f1() noexcept(noexcept(x));
+  T x;
+};
+
+struct S7 {
+  template<typename U>
+  void f1 () noexcept(noexcept(U(1))) { }
+
+  template<int N>
+  void f2() noexcept(noexcept(N));
+
+  template <typename _Up>
+  void f3(_Up __p) noexcept(noexcept(__p));
+};
+
+void glob();
+void globx() noexcept;
+struct S8 {
+  void f1 () noexcept(noexcept(glob()));
+  void f2 () noexcept(noexcept(globx()));
+};
+
+S8 s8;
+SA(!noexcept(s8.f1()));
+SA(noexcept(s8.f2()));
+
+struct W {
+  constexpr operator bool();
+};
+
+template<typename T>
+struct S9 {
+  S9() noexcept(noexcept(w)) { }
+  S9 &operator=(S9 &&) noexcept(T::X);
+  W w;
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept51.C b/gcc/testsuite/g++.dg/cpp0x/noexcept51.C
new file mode 100644 (file)
index 0000000..a81032f
--- /dev/null
@@ -0,0 +1,14 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void fn1(void());
+template <typename> class A {
+  void _M_local_data();
+  A() noexcept(_M_local_data);
+};
+
+class B {
+  void _S_initialize();
+  static void _S_initialize_once();
+};
+void B::_S_initialize() { fn1(_S_initialize_once); }
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept52.C b/gcc/testsuite/g++.dg/cpp0x/noexcept52.C
new file mode 100644 (file)
index 0000000..12c6d36
--- /dev/null
@@ -0,0 +1,9 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+template <typename _Alloc> class A {
+  typedef _Alloc _Alloc_traits;
+  A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+  A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+  void m_fn2(A<char>) {}
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept53.C b/gcc/testsuite/g++.dg/cpp0x/noexcept53.C
new file mode 100644 (file)
index 0000000..b3859de
--- /dev/null
@@ -0,0 +1,26 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct S {
+  void f1() noexcept(noexcept(fn()));
+  void f2() noexcept(noexcept(fnx()));
+  void fn();
+  void fnx() noexcept;
+};
+
+void
+S::f1() noexcept // { dg-error "different exception specifier" }
+{
+}
+
+void
+S::f2() // { dg-error "different exception specifier" }
+{
+}
+
+struct S2 {
+  void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
+  void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
+  void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
+  void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
+};
index 0ba6145ef0c608173070b9a5739625bf11ac72f4..6bccc704d4998a0e9464d8fadd4948bb6bb6ad2f 100644 (file)
@@ -18,7 +18,7 @@ struct D : private B
                                // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
 struct E : public D
 {
-  virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } }
+  virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } }
 };                            // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
                               // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
 B* foo (D *);