diff mbox series

C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)

Message ID 20181219202731.GL21364@redhat.com
State New
Headers show
Series C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869) | expand

Commit Message

Marek Polacek Dec. 19, 2018, 8:27 p.m. UTC
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).

This wasn't as easy as I'd anticipated, because I needed to make sure to
* handle well accessing function parameters in the noexcept-specifier,
  hence the maybe_{begin,end}_member_function_processing business,
* not regress diagnostic.  See e.g. noexcept38.C for detecting "looser
  throw specifier", or noexcept39.C, friend decls and redeclaration.
  This is handled by functions like noexcept_override_late_checks and
  check_redeclaration_exception_specification.  I hope that's it.

Compiling libstdc++ was a fairly good stress test, and I've added a bunch
of reduced testcases I've collected along the way.

I also noticed we're not properly detecting using 'this' in static member
functions; tracked in 88548.

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

2018-12-19  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.
	(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 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.
	(maybe_begin_member_function_processing): New.
	(maybe_end_member_function_processing): 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/noexcept34.C: New test.
	* g++.dg/cpp0x/noexcept35.C: New test.
	* g++.dg/cpp0x/noexcept36.C: New test.
	* g++.dg/cpp0x/noexcept37.C: New test.
	* g++.dg/cpp0x/noexcept38.C: New test.
	* g++.dg/cpp0x/noexcept39.C: New test.

Comments

Marek Polacek Jan. 4, 2019, 2:44 p.m. UTC | #1
Ping.

On Wed, Dec 19, 2018 at 03:27:31PM -0500, 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).
> 
> This wasn't as easy as I'd anticipated, because I needed to make sure to
> * handle well accessing function parameters in the noexcept-specifier,
>   hence the maybe_{begin,end}_member_function_processing business,
> * not regress diagnostic.  See e.g. noexcept38.C for detecting "looser
>   throw specifier", or noexcept39.C, friend decls and redeclaration.
>   This is handled by functions like noexcept_override_late_checks and
>   check_redeclaration_exception_specification.  I hope that's it.
> 
> Compiling libstdc++ was a fairly good stress test, and I've added a bunch
> of reduced testcases I've collected along the way.
> 
> I also noticed we're not properly detecting using 'this' in static member
> functions; tracked in 88548.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2018-12-19  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.
> 	(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 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.
> 	(maybe_begin_member_function_processing): New.
> 	(maybe_end_member_function_processing): 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/noexcept34.C: New test.
> 	* g++.dg/cpp0x/noexcept35.C: New test.
> 	* g++.dg/cpp0x/noexcept36.C: New test.
> 	* g++.dg/cpp0x/noexcept37.C: New test.
> 	* g++.dg/cpp0x/noexcept38.C: New test.
> 	* g++.dg/cpp0x/noexcept39.C: New test.
> 
> diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
> index 43d90eb1efb..aa8b752d8f4 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, the vector
> +   holds 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 1d806b782bd..bd3cd200fcb 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -1193,6 +1193,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;
> @@ -6430,6 +6433,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);
> @@ -6894,6 +6899,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 d6028e3608c..74444db8623 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> @@ -1140,7 +1140,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)
>  {
> @@ -1152,6 +1152,16 @@ 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))
> +    {
> +      vec_safe_push (DEFARG_INSTANTIATIONS (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 b04eb0c5332..92fc39d3968 100644
> --- gcc/cp/except.c
> +++ gcc/cp/except.c
> @@ -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
>  	      || processing_template_decl);
>  
>    return false;
> diff --git gcc/cp/parser.c gcc/cp/parser.c
> index b57e35d04c5..267b3518156 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 *);
> @@ -1968,11 +1974,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);
>  }
>  
> @@ -20315,7 +20324,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);
>      }
> @@ -23058,6 +23073,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
> +maybe_begin_member_function_processing (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 maybe_begin_member_function_processing.  */
> +
> +static void
> +maybe_end_member_function_processing (void)
> +{
> +  pop_bindings_and_leave_scope ();
> +}
> +
>  /* Parse a class-specifier.
>  
>     class-specifier:
> @@ -23368,6 +23422,56 @@ 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)
> +	{
> +	  if (class_type != DECL_CONTEXT (decl))
> +	    {
> +	      if (pushed_scope)
> +		pop_scope (pushed_scope);
> +	      class_type = DECL_CONTEXT (decl);
> +	      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.  */
> +	  maybe_begin_member_function_processing (decl);
> +
> +	  tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> +	  spec = TREE_PURPOSE (spec);
> +	  vec<tree, va_gc> *p = DEFARG_INSTANTIATIONS (spec);
> +	  tree old_decl = (p ? (*p)[0] : NULL_TREE);
> +
> +	  /* 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);
> +
> +	  /* 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.  */
> +	  maybe_end_member_function_processing ();
> +
> +	  /* 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)
>  	{
> @@ -24537,6 +24641,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);
> @@ -24919,6 +25029,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 us 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_INSTANTIATIONS (expr) = NULL;
> +  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:
> @@ -24947,6 +25140,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)
> @@ -28120,7 +28325,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 8bfa3f3b9c4..df0c6c1960e 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 e99de71ea9e..382a14f7d6a 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -24988,8 +24988,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 d700fe328f4..3e6494dd98f 100644
> --- gcc/cp/search.c
> +++ gcc/cp/search.c
> @@ -1850,6 +1850,38 @@ 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 throw specifier for %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.  */
>  
> @@ -1860,7 +1892,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;
>  
> @@ -1944,21 +1975,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 97074dfab56..a7d1e58c73a 100644
> --- gcc/cp/tree.c
> +++ gcc/cp/tree.c
> @@ -2542,6 +2542,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.  */
> @@ -3653,6 +3654,7 @@ cp_tree_equal (tree t1, tree t2)
>      case TEMPLATE_DECL:
>      case IDENTIFIER_NODE:
>      case SSA_NAME:
> +    case DEFAULT_ARG:
>        return false;
>  
>      case BASELINK:
> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> index 64e36efd17e..6fec77d8269 100644
> --- gcc/cp/typeck2.c
> +++ gcc/cp/typeck2.c
> @@ -2302,6 +2302,12 @@ merge_exception_specifiers (tree list, tree add)
>  {
>    tree noex, orig_list;
>  
> +  /* 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/noexcept34.C gcc/testsuite/g++.dg/cpp0x/noexcept34.C
> new file mode 100644
> index 00000000000..43b38c2446f
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept34.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/noexcept35.C gcc/testsuite/g++.dg/cpp0x/noexcept35.C
> new file mode 100644
> index 00000000000..b3859de9ebc
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept35.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/noexcept36.C gcc/testsuite/g++.dg/cpp0x/noexcept36.C
> new file mode 100644
> index 00000000000..12c6d364099
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept36.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/noexcept37.C gcc/testsuite/g++.dg/cpp0x/noexcept37.C
> new file mode 100644
> index 00000000000..a81032f28e9
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept37.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/noexcept38.C gcc/testsuite/g++.dg/cpp0x/noexcept38.C
> new file mode 100644
> index 00000000000..9e5545bc022
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept38.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 throw specifier" }
> +  void h() noexcept(false);
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept39.C gcc/testsuite/g++.dg/cpp0x/noexcept39.C
> new file mode 100644
> index 00000000000..da7490d651c
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept39.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);
> +};

Marek
Jason Merrill Jan. 7, 2019, 3:44 p.m. UTC | #2
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.

> +  /* 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?

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.

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

> @@ -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?

> +/* 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?

> +/* Undo the effects of maybe_begin_member_function_processing.  */
> +
> +static void
> +maybe_end_member_function_processing (void)

And then perhaps pop_injected_parms.

> +/* 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".

Jason
Marek Polacek May 10, 2019, 7:21 p.m. UTC | #3
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  <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 (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 Polacek May 17, 2019, 2:35 p.m. UTC | #4
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  <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 (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 Polacek May 24, 2019, 4:17 p.m. UTC | #5
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  <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 (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
Jason Merrill May 28, 2019, 3:46 p.m. UTC | #6
On 5/10/19 3:21 PM, 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.

OK, fair enough.

> @@ -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);

Can we handle this in cp_parser_save_default_args rather than all its 
callers?

> +/* 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 ();
> +}

Can we share this code with store_parm_decls instead of having two copies?

> @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> +      /* [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);

We might optimize the noexcept(<literal>) case, which should be pretty 
common.

> +maybe_check_throw_specifier (tree overrider, tree basefn)

maybe_check_overriding_exception_spec

> 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;

Here you're throwing away the other side, which seems wrong.

Jason
Marek Polacek June 4, 2019, 1:01 a.m. UTC | #7
On Tue, May 28, 2019 at 11:46:53AM -0400, Jason Merrill wrote:
> > @@ -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);
> 
> Can we handle this in cp_parser_save_default_args rather than all its
> callers?

Yes, 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
> > +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 ();
> > +}
> 
> Can we share this code with store_parm_decls instead of having two copies?

Makes sense, the nreverse logic is tricky enough not to duplicate it.  I named
the new function do_push_parm_decls.

> > @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> > +      /* [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);
> 
> We might optimize the noexcept(<literal>) case, which should be pretty
> common.

Yeah, it's worth it.  Added for numbers/false/true.

> > +maybe_check_throw_specifier (tree overrider, tree basefn)
> 
> maybe_check_overriding_exception_spec

Changed.

> > 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;
> 
> Here you're throwing away the other side, which seems wrong.

I sort of ended up going down a rathole, but then I realized we don't need to
delay parsing of noexcept-specifiers of member friend function declarations,
because they aren't members of the class.  This was a huge relief because
member friend function declarations can be redeclared, so we'd have to make
sure to check if their noexcept-specifiers match.  But member function decls
can't be redeclared.  I updated the comment to better reflect why what I'm
doing there is correct, along with an assert.

noexcept47.C tests various cases with friend declarations.

Thanks,

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

2019-06-03  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 (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, do_push_parm_decls,
	maybe_check_overriding_exception_spec): Declare.
	* decl.c (check_redeclaration_exception_specification): No longer
	static.  Handle UNPARSED_NOEXCEPT_SPEC_P.
	(do_push_parm_decls): New function, broken out of...
	(store_parm_decls): ...here.
	* 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.
	* 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/cpp0x/noexcept47.C: New test.
	* g++.dg/cpp0x/noexcept48.C: New test.
	* g++.dg/cpp0x/noexcept49.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 4d79c43c5af..3f1d8a853db 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -1187,12 +1187,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;
 };
 
 
@@ -1206,6 +1214,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 +6478,9 @@ 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);
+extern void do_push_parm_decls			(tree, tree, tree *);
 
 /* in decl2.c */
 extern void record_mangling			(tree, bool);
@@ -6932,6 +6947,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.  */
 
diff --git gcc/cp/decl.c gcc/cp/decl.c
index bdf397e5ecb..e8fdc08bd4a 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,18 @@ check_redeclaration_exception_specification (tree new_decl,
       && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
     return;
 
+  /* We can't compare unparsed noexcept-specifiers.  Save the 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;
+    }
+  else
+    /* Member functions can't be redeclared.  */
+    gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (old_exceptions));
+
   if (!type_dependent_expression_p (old_decl))
     {
       maybe_instantiate_noexcept (new_decl);
@@ -15706,6 +15718,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.
@@ -15716,7 +15761,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;
@@ -15731,35 +15775,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;
diff --git gcc/cp/except.c gcc/cp/except.c
index 892d5201da9..8d7b1e9bac7 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -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;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 1de35da83ec..ea4adbcdb14 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, 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 *);
 
@@ -20804,7 +20813,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);
 
@@ -23298,6 +23307,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:
@@ -23608,6 +23645,67 @@ 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);
+
+	  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);
+
+	  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);
+
+	  /* 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)
 	{
@@ -25163,6 +25261,91 @@ 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,
+					    /*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:
@@ -25173,13 +25356,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;
@@ -25191,6 +25376,26 @@ 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_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
+	  /* No need to delay parsing for a number literal or true/false.  */
+	  && !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_keyword (parser->lexer, 3, RID_FALSE)
+		   || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
+						     RID_TRUE)))
+	  && 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)
@@ -25261,10 +25466,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;
@@ -25274,8 +25480,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;
 
@@ -28403,7 +28613,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
@@ -28696,6 +28906,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,
@@ -40506,7 +40721,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);
@@ -40566,8 +40785,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)
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 cfbd9fd4c88..0324bd404e9 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -25302,8 +25302,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 dac08d44d76..372c4424747 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -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.  */
diff --git gcc/cp/tree.c gcc/cp/tree.c
index cd021b7f594..81c53b23ebf 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -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:
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index e9f759d4213..eec5550bf2d 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -2391,6 +2391,15 @@ 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.
+     We can use the newer variant, because the old one will be saved in
+     DEFARG_DECL's noexcept-specifier.  */
+  if (UNPARSED_NOEXCEPT_SPEC_P (list))
+    return list;
+  else
+    /* Member functions can't be redeclared.  */
+    gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (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/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C
new file mode 100644
index 00000000000..0848e68f9b1
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C
@@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C
new file mode 100644
index 00000000000..134212c3613
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C
@@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C
new file mode 100644
index 00000000000..6da7ff3361f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C
@@ -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 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 Polacek June 10, 2019, 12:28 p.m. UTC | #8
Ping.

On Mon, Jun 03, 2019 at 09:01:37PM -0400, Marek Polacek wrote:
> On Tue, May 28, 2019 at 11:46:53AM -0400, Jason Merrill wrote:
> > > @@ -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);
> > 
> > Can we handle this in cp_parser_save_default_args rather than all its
> > callers?
> 
> Yes, 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
> > > +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 ();
> > > +}
> > 
> > Can we share this code with store_parm_decls instead of having two copies?
> 
> Makes sense, the nreverse logic is tricky enough not to duplicate it.  I named
> the new function do_push_parm_decls.
> 
> > > @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> > > +      /* [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);
> > 
> > We might optimize the noexcept(<literal>) case, which should be pretty
> > common.
> 
> Yeah, it's worth it.  Added for numbers/false/true.
> 
> > > +maybe_check_throw_specifier (tree overrider, tree basefn)
> > 
> > maybe_check_overriding_exception_spec
> 
> Changed.
> 
> > > 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;
> > 
> > Here you're throwing away the other side, which seems wrong.
> 
> I sort of ended up going down a rathole, but then I realized we don't need to
> delay parsing of noexcept-specifiers of member friend function declarations,
> because they aren't members of the class.  This was a huge relief because
> member friend function declarations can be redeclared, so we'd have to make
> sure to check if their noexcept-specifiers match.  But member function decls
> can't be redeclared.  I updated the comment to better reflect why what I'm
> doing there is correct, along with an assert.
> 
> noexcept47.C tests various cases with friend declarations.
> 
> Thanks,
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2019-06-03  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 (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, do_push_parm_decls,
> 	maybe_check_overriding_exception_spec): Declare.
> 	* decl.c (check_redeclaration_exception_specification): No longer
> 	static.  Handle UNPARSED_NOEXCEPT_SPEC_P.
> 	(do_push_parm_decls): New function, broken out of...
> 	(store_parm_decls): ...here.
> 	* 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.
> 	* 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/cpp0x/noexcept47.C: New test.
> 	* g++.dg/cpp0x/noexcept48.C: New test.
> 	* g++.dg/cpp0x/noexcept49.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 4d79c43c5af..3f1d8a853db 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -1187,12 +1187,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;
>  };
>  
>  
> @@ -1206,6 +1214,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 +6478,9 @@ 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);
> +extern void do_push_parm_decls			(tree, tree, tree *);
>  
>  /* in decl2.c */
>  extern void record_mangling			(tree, bool);
> @@ -6932,6 +6947,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.  */
>  
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index bdf397e5ecb..e8fdc08bd4a 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,18 @@ check_redeclaration_exception_specification (tree new_decl,
>        && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
>      return;
>  
> +  /* We can't compare unparsed noexcept-specifiers.  Save the 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;
> +    }
> +  else
> +    /* Member functions can't be redeclared.  */
> +    gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (old_exceptions));
> +
>    if (!type_dependent_expression_p (old_decl))
>      {
>        maybe_instantiate_noexcept (new_decl);
> @@ -15706,6 +15718,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.
> @@ -15716,7 +15761,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;
> @@ -15731,35 +15775,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;
> diff --git gcc/cp/except.c gcc/cp/except.c
> index 892d5201da9..8d7b1e9bac7 100644
> --- gcc/cp/except.c
> +++ gcc/cp/except.c
> @@ -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;
> diff --git gcc/cp/parser.c gcc/cp/parser.c
> index 1de35da83ec..ea4adbcdb14 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, 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 *);
>  
> @@ -20804,7 +20813,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);
>  
> @@ -23298,6 +23307,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:
> @@ -23608,6 +23645,67 @@ 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);
> +
> +	  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);
> +
> +	  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);
> +
> +	  /* 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)
>  	{
> @@ -25163,6 +25261,91 @@ 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,
> +					    /*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:
> @@ -25173,13 +25356,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;
> @@ -25191,6 +25376,26 @@ 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_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
> +	  /* No need to delay parsing for a number literal or true/false.  */
> +	  && !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_keyword (parser->lexer, 3, RID_FALSE)
> +		   || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
> +						     RID_TRUE)))
> +	  && 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)
> @@ -25261,10 +25466,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;
> @@ -25274,8 +25480,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;
>  
> @@ -28403,7 +28613,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
> @@ -28696,6 +28906,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,
> @@ -40506,7 +40721,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);
> @@ -40566,8 +40785,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)
> 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 cfbd9fd4c88..0324bd404e9 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -25302,8 +25302,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 dac08d44d76..372c4424747 100644
> --- gcc/cp/search.c
> +++ gcc/cp/search.c
> @@ -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.  */
> diff --git gcc/cp/tree.c gcc/cp/tree.c
> index cd021b7f594..81c53b23ebf 100644
> --- gcc/cp/tree.c
> +++ gcc/cp/tree.c
> @@ -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:
> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> index e9f759d4213..eec5550bf2d 100644
> --- gcc/cp/typeck2.c
> +++ gcc/cp/typeck2.c
> @@ -2391,6 +2391,15 @@ 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.
> +     We can use the newer variant, because the old one will be saved in
> +     DEFARG_DECL's noexcept-specifier.  */
> +  if (UNPARSED_NOEXCEPT_SPEC_P (list))
> +    return list;
> +  else
> +    /* Member functions can't be redeclared.  */
> +    gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (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/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C
> new file mode 100644
> index 00000000000..0848e68f9b1
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C
> @@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C
> new file mode 100644
> index 00000000000..134212c3613
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C
> @@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C
> new file mode 100644
> index 00000000000..6da7ff3361f
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C
> @@ -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 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
Jason Merrill June 12, 2019, 3:46 a.m. UTC | #9
On 6/3/19 9:01 PM, Marek Polacek wrote:

> I sort of ended up going down a rathole, but then I realized we don't need to
> delay parsing of noexcept-specifiers of member friend function declarations,
> because they aren't members of the class.

Where are you getting this from?  I'm definitely sympathetic to the idea 
that noexcept-specifiers of friend functions shouldn't need to be 
complete-class contexts, but 10.3 doesn't make that distinction that I 
can see.

> This was a huge relief because
> member friend function declarations can be redeclared, so we'd have to make
> sure to check if their noexcept-specifiers match.  But member function decls
> can't be redeclared.  I updated the comment to better reflect why what I'm
> doing there is correct, along with an assert.

But then why do you still need this:

> +  /* We can't compare unparsed noexcept-specifiers.  Save the 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;
> +    }

?

Jason
Marek Polacek June 14, 2019, 8:54 p.m. UTC | #10
On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote:
> On 6/3/19 9:01 PM, Marek Polacek wrote:
> 
> > I sort of ended up going down a rathole, but then I realized we don't need to
> > delay parsing of noexcept-specifiers of member friend function declarations,
> > because they aren't members of the class.
> 
> Where are you getting this from?  I'm definitely sympathetic to the idea
> that noexcept-specifiers of friend functions shouldn't need to be
> complete-class contexts, but 10.3 doesn't make that distinction that I can
> see.

When I tested my patch I noticed that none of the 3 compilers I tried handled
this scenario, so I thought I was missing something.  But if the standard
really doesn't say that noexcept-specifiers of friend functions don't have to
be complete-class contexts, then perhaps it needs to say so.  Should I raise
this on the reflector?

> > This was a huge relief because
> > member friend function declarations can be redeclared, so we'd have to make
> > sure to check if their noexcept-specifiers match.  But member function decls
> > can't be redeclared.  I updated the comment to better reflect why what I'm
> > doing there is correct, along with an assert.
> 
> But then why do you still need this:
> 
> > +  /* We can't compare unparsed noexcept-specifiers.  Save the 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;
> > +    }
> 
> ?

Eh... I don't.  The following version is with the DEFARG_DECL junk removed.

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

2019-06-14  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.

	* 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/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/eh/shadow1.C: Adjust dg-error.

diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 03c105b5c4c..475c584fd4c 100644
--- gcc/cp/cp-tree.def
+++ gcc/cp/cp-tree.def
@@ -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
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 1f4e1e15554..107a322cc82 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -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.  */
 
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 0a3ef452536..43d7ba6a114 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -15720,6 +15720,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.
@@ -15730,7 +15763,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;
@@ -15745,35 +15777,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;
diff --git gcc/cp/except.c gcc/cp/except.c
index 892d5201da9..8d7b1e9bac7 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -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;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 8f5ae84670a..0d926cc0bd5 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, 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,26 @@ 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_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
+	  /* No need to delay parsing for a number literal or true/false.  */
+	  && !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_keyword (parser->lexer, 3, RID_FALSE)
+		   || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
+						     RID_TRUE)))
+	  && 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 +25469,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 +25483,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;
 
@@ -28415,7 +28616,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
@@ -28708,6 +28909,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,
@@ -40579,7 +40785,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);
@@ -40639,8 +40849,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)
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 2a626526c6f..05bc4a3546a 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -25307,8 +25307,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 dac08d44d76..372c4424747 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -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.  */
diff --git gcc/cp/tree.c gcc/cp/tree.c
index cd021b7f594..81c53b23ebf 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -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:
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/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C
new file mode 100644
index 00000000000..0848e68f9b1
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C
@@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C
new file mode 100644
index 00000000000..134212c3613
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C
@@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C
new file mode 100644
index 00000000000..6da7ff3361f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C
@@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept50.C gcc/testsuite/g++.dg/cpp0x/noexcept50.C
new file mode 100644
index 00000000000..43b38c2446f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept50.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/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 *);
Jason Merrill June 21, 2019, 8:47 p.m. UTC | #11
On 6/14/19 4:54 PM, Marek Polacek wrote:
> On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote:
>> On 6/3/19 9:01 PM, Marek Polacek wrote:
>>
>>> I sort of ended up going down a rathole, but then I realized we don't need to
>>> delay parsing of noexcept-specifiers of member friend function declarations,
>>> because they aren't members of the class.
>>
>> Where are you getting this from?  I'm definitely sympathetic to the idea
>> that noexcept-specifiers of friend functions shouldn't need to be
>> complete-class contexts, but 10.3 doesn't make that distinction that I can
>> see.
> 
> When I tested my patch I noticed that none of the 3 compilers I tried handled
> this scenario, so I thought I was missing something.  But if the standard
> really doesn't say that noexcept-specifiers of friend functions don't have to
> be complete-class contexts, then perhaps it needs to say so.  Should I raise
> this on the reflector?

Sounds good.

>>> This was a huge relief because
>>> member friend function declarations can be redeclared, so we'd have to make
>>> sure to check if their noexcept-specifiers match.  But member function decls
>>> can't be redeclared.  I updated the comment to better reflect why what I'm
>>> doing there is correct, along with an assert.
>>
>> But then why do you still need this:
>>
>>> +  /* We can't compare unparsed noexcept-specifiers.  Save the 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;
>>> +    }
>>
>> ?
> 
> Eh... I don't.  The following version is with the DEFARG_DECL junk removed.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2019-06-14  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.

I'd still like to rename this, can you do that in a follow-up?

> @@ -25203,6 +25379,26 @@ 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_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
> +	  /* No need to delay parsing for a number literal or true/false.  */
> +	  && !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_keyword (parser->lexer, 3, RID_FALSE)
> +		   || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
> +						     RID_TRUE)))

Maybe do immediate parsing for any keyword, not just true/false?  I 
can't think of a keyword that delayed parsing would make a difference for.

I think we also need to check that token 4 is close paren, so we still 
get delayed parsing for noexcept (1 + foo).

Jason
Marek Polacek June 21, 2019, 9:29 p.m. UTC | #12
On Fri, Jun 21, 2019 at 04:47:46PM -0400, Jason Merrill wrote:
> On 6/14/19 4:54 PM, Marek Polacek wrote:
> > On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote:
> > > On 6/3/19 9:01 PM, Marek Polacek wrote:
> > > 
> > > > I sort of ended up going down a rathole, but then I realized we don't need to
> > > > delay parsing of noexcept-specifiers of member friend function declarations,
> > > > because they aren't members of the class.
> > > 
> > > Where are you getting this from?  I'm definitely sympathetic to the idea
> > > that noexcept-specifiers of friend functions shouldn't need to be
> > > complete-class contexts, but 10.3 doesn't make that distinction that I can
> > > see.
> > 
> > When I tested my patch I noticed that none of the 3 compilers I tried handled
> > this scenario, so I thought I was missing something.  But if the standard
> > really doesn't say that noexcept-specifiers of friend functions don't have to
> > be complete-class contexts, then perhaps it needs to say so.  Should I raise
> > this on the reflector?
> 
> Sounds good.

Will do.

> > > > This was a huge relief because
> > > > member friend function declarations can be redeclared, so we'd have to make
> > > > sure to check if their noexcept-specifiers match.  But member function decls
> > > > can't be redeclared.  I updated the comment to better reflect why what I'm
> > > > doing there is correct, along with an assert.
> > > 
> > > But then why do you still need this:
> > > 
> > > > +  /* We can't compare unparsed noexcept-specifiers.  Save the 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;
> > > > +    }
> > > 
> > > ?
> > 
> > Eh... I don't.  The following version is with the DEFARG_DECL junk removed.
> > 
> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
> > 
> > 2019-06-14  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.
> 
> I'd still like to rename this, can you do that in a follow-up?

Absolutely.

> > @@ -25203,6 +25379,26 @@ 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_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
> > +	  /* No need to delay parsing for a number literal or true/false.  */
> > +	  && !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_keyword (parser->lexer, 3, RID_FALSE)
> > +		   || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
> > +						     RID_TRUE)))
> 
> Maybe do immediate parsing for any keyword, not just true/false?  I can't
> think of a keyword that delayed parsing would make a difference for.

Probably true.

> I think we also need to check that token 4 is close paren, so we still get
> delayed parsing for noexcept (1 + foo).

Indeed, fixed, in a way that makes the whole conditional more readable.

I've renamed some tests, otherwise no changes.

Bootstrap/regtest running on x86_64-linux, ok for trunk if it passes?

2019-06-21  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.

	* 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.

diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 03c105b5c4c..475c584fd4c 100644
--- gcc/cp/cp-tree.def
+++ gcc/cp/cp-tree.def
@@ -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
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 98f7a0c0cd0..2c05e638915 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -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.  */
 
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 98b54d542a0..b7ad6dead6b 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -15755,6 +15755,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.
@@ -15765,7 +15798,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;
@@ -15780,35 +15812,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;
diff --git gcc/cp/except.c gcc/cp/except.c
index 71f5d609f10..1f87c5ab695 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -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;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 5cbc4551d1a..4d4d32973d9 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, 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)
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 69de55369dd..fb89b933524 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -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;
diff --git gcc/cp/search.c gcc/cp/search.c
index dac08d44d76..372c4424747 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -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.  */
diff --git gcc/cp/tree.c gcc/cp/tree.c
index 978aea56193..ebfe362595f 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -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:
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/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C
new file mode 100644
index 00000000000..0848e68f9b1
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C
@@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C
new file mode 100644
index 00000000000..134212c3613
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C
@@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C
new file mode 100644
index 00000000000..6da7ff3361f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C
@@ -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 gcc/testsuite/g++.dg/cpp0x/noexcept50.C gcc/testsuite/g++.dg/cpp0x/noexcept50.C
new file mode 100644
index 00000000000..43b38c2446f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept50.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/noexcept51.C gcc/testsuite/g++.dg/cpp0x/noexcept51.C
new file mode 100644
index 00000000000..a81032f28e9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept51.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/noexcept52.C gcc/testsuite/g++.dg/cpp0x/noexcept52.C
new file mode 100644
index 00000000000..12c6d364099
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept52.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/noexcept53.C gcc/testsuite/g++.dg/cpp0x/noexcept53.C
new file mode 100644
index 00000000000..b3859de9ebc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept53.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/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 *);
Jason Merrill June 22, 2019, 12:28 a.m. UTC | #13
On 6/21/19 5:29 PM, Marek Polacek wrote:
> On Fri, Jun 21, 2019 at 04:47:46PM -0400, Jason Merrill wrote:
>> On 6/14/19 4:54 PM, Marek Polacek wrote:
>>> On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote:
>>>> On 6/3/19 9:01 PM, Marek Polacek wrote:
>>>>
>>>>> I sort of ended up going down a rathole, but then I realized we don't need to
>>>>> delay parsing of noexcept-specifiers of member friend function declarations,
>>>>> because they aren't members of the class.
>>>>
>>>> Where are you getting this from?  I'm definitely sympathetic to the idea
>>>> that noexcept-specifiers of friend functions shouldn't need to be
>>>> complete-class contexts, but 10.3 doesn't make that distinction that I can
>>>> see.
>>>
>>> When I tested my patch I noticed that none of the 3 compilers I tried handled
>>> this scenario, so I thought I was missing something.  But if the standard
>>> really doesn't say that noexcept-specifiers of friend functions don't have to
>>> be complete-class contexts, then perhaps it needs to say so.  Should I raise
>>> this on the reflector?
>>
>> Sounds good.
> 
> Will do.
> 
>>>>> This was a huge relief because
>>>>> member friend function declarations can be redeclared, so we'd have to make
>>>>> sure to check if their noexcept-specifiers match.  But member function decls
>>>>> can't be redeclared.  I updated the comment to better reflect why what I'm
>>>>> doing there is correct, along with an assert.
>>>>
>>>> But then why do you still need this:
>>>>
>>>>> +  /* We can't compare unparsed noexcept-specifiers.  Save the 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;
>>>>> +    }
>>>>
>>>> ?
>>>
>>> Eh... I don't.  The following version is with the DEFARG_DECL junk removed.
>>>
>>> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>>>
>>> 2019-06-14  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.
>>
>> I'd still like to rename this, can you do that in a follow-up?
> 
> Absolutely.
> 
>>> @@ -25203,6 +25379,26 @@ 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_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
>>> +	  /* No need to delay parsing for a number literal or true/false.  */
>>> +	  && !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_keyword (parser->lexer, 3, RID_FALSE)
>>> +		   || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
>>> +						     RID_TRUE)))
>>
>> Maybe do immediate parsing for any keyword, not just true/false?  I can't
>> think of a keyword that delayed parsing would make a difference for.
> 
> Probably true.
> 
>> I think we also need to check that token 4 is close paren, so we still get
>> delayed parsing for noexcept (1 + foo).
> 
> Indeed, fixed, in a way that makes the whole conditional more readable.
> 
> I've renamed some tests, otherwise no changes.
> 
> Bootstrap/regtest running on x86_64-linux, ok for trunk if it passes?

Yes, thanks.

Jason
diff mbox series

Patch

diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 43d90eb1efb..aa8b752d8f4 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, the vector
+   holds 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 1d806b782bd..bd3cd200fcb 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -1193,6 +1193,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;
@@ -6430,6 +6433,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);
@@ -6894,6 +6899,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 d6028e3608c..74444db8623 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -1140,7 +1140,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)
 {
@@ -1152,6 +1152,16 @@  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))
+    {
+      vec_safe_push (DEFARG_INSTANTIATIONS (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 b04eb0c5332..92fc39d3968 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -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
 	      || processing_template_decl);
 
   return false;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index b57e35d04c5..267b3518156 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 *);
@@ -1968,11 +1974,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);
 }
 
@@ -20315,7 +20324,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);
     }
@@ -23058,6 +23073,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
+maybe_begin_member_function_processing (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 maybe_begin_member_function_processing.  */
+
+static void
+maybe_end_member_function_processing (void)
+{
+  pop_bindings_and_leave_scope ();
+}
+
 /* Parse a class-specifier.
 
    class-specifier:
@@ -23368,6 +23422,56 @@  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)
+	{
+	  if (class_type != DECL_CONTEXT (decl))
+	    {
+	      if (pushed_scope)
+		pop_scope (pushed_scope);
+	      class_type = DECL_CONTEXT (decl);
+	      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.  */
+	  maybe_begin_member_function_processing (decl);
+
+	  tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+	  spec = TREE_PURPOSE (spec);
+	  vec<tree, va_gc> *p = DEFARG_INSTANTIATIONS (spec);
+	  tree old_decl = (p ? (*p)[0] : NULL_TREE);
+
+	  /* 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);
+
+	  /* 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.  */
+	  maybe_end_member_function_processing ();
+
+	  /* 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)
 	{
@@ -24537,6 +24641,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);
@@ -24919,6 +25029,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 us 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_INSTANTIATIONS (expr) = NULL;
+  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:
@@ -24947,6 +25140,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)
@@ -28120,7 +28325,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 8bfa3f3b9c4..df0c6c1960e 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 e99de71ea9e..382a14f7d6a 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -24988,8 +24988,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 d700fe328f4..3e6494dd98f 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -1850,6 +1850,38 @@  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 throw specifier for %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.  */
 
@@ -1860,7 +1892,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;
 
@@ -1944,21 +1975,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 97074dfab56..a7d1e58c73a 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -2542,6 +2542,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.  */
@@ -3653,6 +3654,7 @@  cp_tree_equal (tree t1, tree t2)
     case TEMPLATE_DECL:
     case IDENTIFIER_NODE:
     case SSA_NAME:
+    case DEFAULT_ARG:
       return false;
 
     case BASELINK:
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index 64e36efd17e..6fec77d8269 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -2302,6 +2302,12 @@  merge_exception_specifiers (tree list, tree add)
 {
   tree noex, orig_list;
 
+  /* 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/noexcept34.C gcc/testsuite/g++.dg/cpp0x/noexcept34.C
new file mode 100644
index 00000000000..43b38c2446f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept34.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/noexcept35.C gcc/testsuite/g++.dg/cpp0x/noexcept35.C
new file mode 100644
index 00000000000..b3859de9ebc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept35.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/noexcept36.C gcc/testsuite/g++.dg/cpp0x/noexcept36.C
new file mode 100644
index 00000000000..12c6d364099
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept36.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/noexcept37.C gcc/testsuite/g++.dg/cpp0x/noexcept37.C
new file mode 100644
index 00000000000..a81032f28e9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept37.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/noexcept38.C gcc/testsuite/g++.dg/cpp0x/noexcept38.C
new file mode 100644
index 00000000000..9e5545bc022
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept38.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 throw specifier" }
+  void h() noexcept(false);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept39.C gcc/testsuite/g++.dg/cpp0x/noexcept39.C
new file mode 100644
index 00000000000..da7490d651c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept39.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);
+};