Ping.

On Fri, May 17, 2019 at 10:35:29AM -0400, Marek Polacek wrote:
> Ping.
> 
> On Fri, May 10, 2019 at 03:21:33PM -0400, Marek Polacek wrote:
> > Coming back to this.  I didn't think this was suitable for GCC 9.
> > 
> > On Mon, Jan 07, 2019 at 10:44:37AM -0500, Jason Merrill wrote:
> > > On 12/19/18 3:27 PM, Marek Polacek wrote:
> > > > Prompted by Jon's observation in 52869, I noticed that we don't treat
> > > > a noexcept-specifier as a complete-class context of a class 
> > > > ([class.mem]/6).
> > > > As with member function bodies, default arguments, and NSDMIs, names 
> > > > used in
> > > > a noexcept-specifier of a member-function can be declared later in the 
> > > > class
> > > > body, so we need to wait and parse them at the end of the class.
> > > > For that, I've made use of DEFAULT_ARG (now best to be renamed to 
> > > > UNPARSED_ARG).
> > > 
> > > Or DEFERRED_PARSE, yes.
> > 
> > I didn't change the name but I'm happy to do it as a follow up.
> > 
> > > > +  /* We can't compare unparsed noexcept-specifiers.  Save the old decl
> > > > +     and check this again after we've parsed the noexcept-specifiers
> > > > +     for real.  */
> > > > +  if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> > > > +    {
> > > > +      vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE 
> > > > (new_exceptions)),
> > > > +                    copy_decl (old_decl));
> > > > +      return;
> > > > +    }
> > > 
> > > Why copy_decl?
> > 
> > This is so that we don't lose the diagnostic in noexcept46.C.  If I don't 
> > use
> > copy_decl then the tree is shared and subsequent changes to it make us not
> > detect discrepancies like noexcept(false) vs. noexcept(true) on the same 
> > decl.
> > 
> > > It seems wasteful to allocate a vec to hold this single decl; let's make 
> > > the
> > > last field of tree_default_arg a union instead.  And add a new macro for 
> > > the
> > > single decl case.
> > 
> > Done.  But that required also adding GTY markers *and* a new BOOL_BITFIELD 
> > for
> > the sake of GTY((desc)).
> > 
> > > I notice that default_arg currently uses tree_common for some reason, and 
> > > we
> > > ought to be able to save two words by switching to tree_base
> > 
> > Done.
> > 
> > > > @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
> > > >               || TREE_VALUE (spec)
> > > >               || spec == noexcept_false_spec
> > > >               || TREE_PURPOSE (spec) == error_mark_node
> > > > +             || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG
> > > 
> > > Maybe use UNPARSED_NOEXCEPT_SPEC_P here?
> >  
> > Done.
> > 
> > > > +/* 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
> > > > +maybe_begin_member_function_processing (tree decl)
> > > 
> > > This name is pretty misleading.  How about inject_parm_decls, to go with
> > > inject_this_parameter?
> > 
> > Done.
> > 
> > > > +/* Undo the effects of maybe_begin_member_function_processing.  */
> > > > +
> > > > +static void
> > > > +maybe_end_member_function_processing (void)
> > > 
> > > And then perhaps pop_injected_parms.
> > 
> > Done.
> > 
> > > > +/* Check throw specifier of OVERRIDER is at least as strict as
> > > > +   the one of BASEFN.  */
> > > > +
> > > > +bool
> > > > +maybe_check_throw_specifier (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 throw specifier for %q+#F", overrider);
> > > 
> > > Since we're touching this diagnostic, let's correct it now to "exception
> > > specification".  And add "on overriding virtual function".
> > 
> > Ok, changed to the more up-to-date term.
> > 
> > Two further changes were required since my changes to detecting 'this' for
> > static member functions:
> > 1) use THIS_FORBIDDEN if needed when parsing the delayed noexcept,
> > 2) careful about friend member functions -- its DECL_CONTEXT is not the
> >   containing class, need to use DECL_FRIEND_CONTEXT.
> > 
> > Both of these points are tested in g++.dg/cpp0x/this1.C.
> > 
> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
> > 
> > 2019-05-10  Marek Polacek  <pola...@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 (DEFARG_DECL, DEFARG_NOEXCEPT_P, UNPARSED_NOEXCEPT_SPEC_P):
> >     New macros.
> >     (tree_default_arg): Add a tree field, make the last two fields into a
> >     union.  Add GTY markers.
> >     (check_redeclaration_exception_specification): Declare.
> >     (maybe_check_throw_specifier): Declare.
> >     * decl.c (check_redeclaration_exception_specification): No longer
> >     static.  Handle UNPARSED_NOEXCEPT_SPEC_P.
> >     * 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_init_declarator): Maybe save the noexcept-specifier to
> >     post process.
> >     (inject_parm_decls): New.
> >     (pop_injected_parms): New.
> >     (cp_parser_class_specifier_1): Implement delayed parsing of
> >     noexcept-specifiers.
> >     (cp_parser_member_declaration): Maybe save the noexcept-specifier to
> >     post process.
> >     (cp_parser_save_noexcept): New.
> >     (cp_parser_late_noexcept_specifier): New.
> >     (noexcept_override_late_checks): New.
> >     (cp_parser_noexcept_specification_opt): Call cp_parser_save_noexcept
> >     instead of the normal processing if needed.
> >     (cp_parser_save_member_function_body): Maybe save the
> >     noexcept-specifier to post process.
> >     * 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_throw_specifier): New function, broken out
> >     of...
> >     (check_final_overrider): ...here.  Call maybe_check_throw_specifier.
> >     * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
> >     (cp_tree_equal): Handle DEFAULT_ARG.
> >     * typeck2.c (merge_exception_specifiers): If an unparsed noexcept
> >     expression has been passed, return it instead of merging it.
> > 
> >     * g++.dg/cpp0x/noexcept41.C: New test.
> >     * g++.dg/cpp0x/noexcept42.C: New test.
> >     * g++.dg/cpp0x/noexcept43.C: New test.
> >     * g++.dg/cpp0x/noexcept44.C: New test.
> >     * g++.dg/cpp0x/noexcept45.C: New test.
> >     * g++.dg/cpp0x/noexcept46.C: New test.
> >     * g++.dg/eh/shadow1.C: Adjust dg-error.
> > 
> > diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
> > index 03c105b5c4c..33eb5d25efe 100644
> > --- gcc/cp/cp-tree.def
> > +++ gcc/cp/cp-tree.def
> > @@ -209,7 +209,9 @@ 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.  For a noexcept-specifier, we use a tree
> > +   holding a function declaration used for late checking.  */
> >  DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
> >  
> >  /* An uninstantiated/unevaluated noexcept-specification.  For the
> > diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> > index f253857b02a..ef14a011293 100644
> > --- gcc/cp/cp-tree.h
> > +++ gcc/cp/cp-tree.h
> > @@ -1178,12 +1178,20 @@ enum cp_identifier_kind {
> >  #define DEFARG_TOKENS(NODE) \
> >    (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->tokens)
> >  #define DEFARG_INSTANTIATIONS(NODE) \
> > -  (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
> > +  (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.instantiations)
> > +#define DEFARG_DECL(NODE) \
> > +  (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.decl)
> > +#define DEFARG_NOEXCEPT_P(NODE) \
> > +  (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->noexcept_p)
> >  
> >  struct GTY (()) tree_default_arg {
> > -  struct tree_common common;
> > +  struct tree_base base;
> >    struct cp_token_cache *tokens;
> > -  vec<tree, va_gc> *instantiations;
> > +  BOOL_BITFIELD noexcept_p : 1;
> > +  union {
> > +    vec<tree, va_gc>* GTY((tag ("0"))) instantiations;
> > +    tree GTY((tag ("1"))) decl;
> > +  } GTY((desc ("%1.noexcept_p"))) u;
> >  };
> >  
> >  
> > @@ -1197,6 +1205,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;
> > @@ -6464,6 +6475,8 @@ 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 check_redeclaration_exception_specification
> > +  (tree, tree);
> >  
> >  /* in decl2.c */
> >  extern void record_mangling                        (tree, bool);
> > @@ -6929,6 +6942,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_throw_specifier            (tree, tree);
> >  
> >  /* The representation of a deferred access check.  */
> >  
> > diff --git gcc/cp/decl.c gcc/cp/decl.c
> > index 36014dc628e..a2effa13623 100644
> > --- gcc/cp/decl.c
> > +++ gcc/cp/decl.c
> > @@ -1139,7 +1139,7 @@ warn_extern_redeclared_static (tree newdecl, tree 
> > olddecl)
> >     function templates.  If their exception specifications do not
> >     match, issue a diagnostic.  */
> >  
> > -static void
> > +void
> >  check_redeclaration_exception_specification (tree new_decl,
> >                                          tree old_decl)
> >  {
> > @@ -1151,6 +1151,15 @@ check_redeclaration_exception_specification (tree 
> > new_decl,
> >        && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
> >      return;
> >  
> > +  /* We can't compare unparsed noexcept-specifiers.  Save the old decl
> > +     and check this again after we've parsed the noexcept-specifiers
> > +     for real.  */
> > +  if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> > +    {
> > +      DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
> > +      return;
> > +    }
> > +
> >    if (!type_dependent_expression_p (old_decl))
> >      {
> >        maybe_instantiate_noexcept (new_decl);
> > diff --git gcc/cp/except.c gcc/cp/except.c
> > index afc261073d7..208c9c1461d 100644
> > --- gcc/cp/except.c
> > +++ gcc/cp/except.c
> > @@ -1248,6 +1248,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;
> > diff --git gcc/cp/parser.c gcc/cp/parser.c
> > index 12beadf5096..41197ab3486 100644
> > --- gcc/cp/parser.c
> > +++ gcc/cp/parser.c
> > @@ -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);
> > +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);
> >  }
> >  
> > @@ -20515,7 +20524,13 @@ cp_parser_init_declarator (cp_parser* parser,
> >                     /*asmspec=*/NULL_TREE,
> >                     attr_chainon (attributes, prefix_attributes));
> >        if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> > -   cp_parser_save_default_args (parser, decl);
> > +   {
> > +     cp_parser_save_default_args (parser, decl);
> > +     /* 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);
> > +   }
> >        cp_finalize_omp_declare_simd (parser, decl);
> >        cp_finalize_oacc_routine (parser, decl, false);
> >      }
> > @@ -23334,6 +23349,45 @@ 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);
> > +  args = nreverse (args);
> > +
> > +  tree next;
> > +  for (tree parm = args; parm; parm = next)
> > +    {
> > +      next = DECL_CHAIN (parm);
> > +      if (TREE_CODE (parm) == PARM_DECL)
> > +   pushdecl (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 ();
> > +}
> > +
> > +/* Undo the effects of inject_parm_decls.  */
> > +
> > +static void
> > +pop_injected_parms (void)
> > +{
> > +  pop_bindings_and_leave_scope ();
> > +}
> > +
> >  /* Parse a class-specifier.
> >  
> >     class-specifier:
> > @@ -23644,6 +23698,66 @@ 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_FRIEND_P (decl) ? DECL_FRIEND_CONTEXT (decl)
> > +                 : DECL_CONTEXT (decl));
> > +     if (class_type != ctx)
> > +       {
> > +         if (pushed_scope)
> > +           pop_scope (pushed_scope);
> > +         class_type = ctx;
> > +         pushed_scope = push_scope (class_type);
> > +       }
> > +
> > +     /* 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);
> > +
> > +     tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> > +     spec = TREE_PURPOSE (spec);
> > +     tree old_decl = DEFARG_DECL (spec);
> > +
> > +     /* '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);
> > +
> > +     /* Restore the state of local_variables_forbidden_p.  */
> > +     parser->local_variables_forbidden_p = local_variables_forbidden_p;
> > +     if (spec != error_mark_node)
> > +       TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
> > +
> > +     /* If we've stashed an old declaration, it means we need to
> > +        perform late redeclaration checking.  */
> > +     if (old_decl)
> > +       check_redeclaration_exception_specification (decl, old_decl);
> > +
> > +     /* 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)
> >     {
> > @@ -24817,6 +24931,12 @@ cp_parser_member_declaration (cp_parser* parser)
> >               else
> >                 decl = finish_fully_implicit_template (parser, decl);
> >             }
> > +         if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> > +           {
> > +             tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> > +             if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> > +               vec_safe_push (unparsed_noexcepts, decl);
> > +           }
> >         }
> >  
> >       cp_finalize_omp_declare_simd (parser, decl);
> > @@ -25199,6 +25319,90 @@ 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);
> > +  DEFARG_DECL (expr) = NULL_TREE;
> > +  DEFARG_NOEXCEPT_P (expr) = true;
> > +  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,
> > +                                       NULL,
> > +                                       /*return_cond=*/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_throw_specifier (fndecl, fn);
> > +    }
> > +}
> > +
> >  /* Parse an (optional) noexcept-specification.
> >  
> >     noexcept-specification:
> > @@ -25227,6 +25431,18 @@ 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.  */
> > +      if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == 
> > CPP_OPEN_PAREN
> > +     && at_class_scope_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)
> > @@ -28427,7 +28643,12 @@ cp_parser_save_member_function_body (cp_parser* 
> > parser,
> >        return error_mark_node;
> >      }
> >  
> > -  /* Remember it, if there default args to post process.  */
> > +  /* Remember if there is a noexcept-specifier to post process.  */
> > +  tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
> > +  if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> > +    vec_safe_push (unparsed_noexcepts, fn);
> > +
> > +  /* 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
> > diff --git gcc/cp/parser.h gcc/cp/parser.h
> > index c03a9d87af5..2890788f489 100644
> > --- gcc/cp/parser.h
> > +++ gcc/cp/parser.h
> > @@ -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;
> >  };
> >  
> >  
> > diff --git gcc/cp/pt.c gcc/cp/pt.c
> > index d6976e08690..c00d14fd954 100644
> > --- gcc/cp/pt.c
> > +++ gcc/cp/pt.c
> > @@ -25308,8 +25308,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;
> > diff --git gcc/cp/search.c gcc/cp/search.c
> > index 4c3fffda717..5a3a0cf2824 100644
> > --- gcc/cp/search.c
> > +++ gcc/cp/search.c
> > @@ -1863,6 +1863,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_throw_specifier (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.  */
> >  
> > @@ -1873,7 +1906,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;
> >  
> > @@ -1957,21 +1989,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_throw_specifier (overrider, basefn))
> > +    return 0;
> >  
> >    /* Check for conflicting type attributes.  But leave transaction_safe for
> >       set_one_vmethod_tm_attributes.  */
> > diff --git gcc/cp/tree.c gcc/cp/tree.c
> > index 718eed349c6..bc0080d6720 100644
> > --- gcc/cp/tree.c
> > +++ gcc/cp/tree.c
> > @@ -2550,6 +2550,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.  */
> > @@ -3662,6 +3663,7 @@ cp_tree_equal (tree t1, tree t2)
> >      case IDENTIFIER_NODE:
> >      case SSA_NAME:
> >      case USING_DECL:
> > +    case DEFAULT_ARG:
> >        return false;
> >  
> >      case BASELINK:
> > diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> > index df002a1664c..8cbc48fb44f 100644
> > --- gcc/cp/typeck2.c
> > +++ gcc/cp/typeck2.c
> > @@ -2393,6 +2393,12 @@ merge_exception_specifiers (tree list, tree add)
> >    if (list == error_mark_node || add == error_mark_node)
> >      return error_mark_node;
> >  
> > +  /* We don't want to lose the unparsed operand lest we miss diagnostics.  
> > */
> > +  if (UNPARSED_NOEXCEPT_SPEC_P (list))
> > +    return list;
> > +  else if (UNPARSED_NOEXCEPT_SPEC_P (add))
> > +    return add;
> > +
> >    /* No exception-specifier or noexcept(false) are less strict than
> >       anything else.  Prefer the newer variant (LIST).  */
> >    if (!list || list == noexcept_false_spec)
> > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept41.C 
> > gcc/testsuite/g++.dg/cpp0x/noexcept41.C
> > new file mode 100644
> > index 00000000000..43b38c2446f
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept41.C
> > @@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept42.C 
> > gcc/testsuite/g++.dg/cpp0x/noexcept42.C
> > new file mode 100644
> > index 00000000000..b3859de9ebc
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept42.C
> > @@ -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" }
> > +};
> > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept43.C 
> > gcc/testsuite/g++.dg/cpp0x/noexcept43.C
> > new file mode 100644
> > index 00000000000..12c6d364099
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept43.C
> > @@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept44.C 
> > gcc/testsuite/g++.dg/cpp0x/noexcept44.C
> > new file mode 100644
> > index 00000000000..a81032f28e9
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept44.C
> > @@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept45.C 
> > gcc/testsuite/g++.dg/cpp0x/noexcept45.C
> > new file mode 100644
> > index 00000000000..39df4a6571e
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C
> > @@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept46.C 
> > gcc/testsuite/g++.dg/cpp0x/noexcept46.C
> > new file mode 100644
> > index 00000000000..da7490d651c
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C
> > @@ -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 gcc/testsuite/g++.dg/eh/shadow1.C 
> > gcc/testsuite/g++.dg/eh/shadow1.C
> > index 0ba6145ef0c..6bccc704d49 100644
> > --- gcc/testsuite/g++.dg/eh/shadow1.C
> > +++ gcc/testsuite/g++.dg/eh/shadow1.C
> > @@ -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 *);
> 
> Marek

Marek

Reply via email to