public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Marek Polacek <polacek@redhat.com>
To: GCC Patches <gcc-patches@gcc.gnu.org>, Jason Merrill <jason@redhat.com>
Subject: Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
Date: Fri, 04 Jan 2019 14:45:00 -0000	[thread overview]
Message-ID: <20190104144446.GD21364@redhat.com> (raw)
In-Reply-To: <20181219202731.GL21364@redhat.com>

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

  reply	other threads:[~2019-01-04 14:45 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-19 20:27 Marek Polacek
2019-01-04 14:45 ` Marek Polacek [this message]
2019-01-07 15:44 ` Jason Merrill
2019-05-10 19:21   ` Marek Polacek
2019-05-17 14:35     ` Marek Polacek
2019-05-24 16:17       ` Marek Polacek
2019-05-28 15:48     ` Jason Merrill
2019-06-04  1:02       ` Marek Polacek
2019-06-10 12:28         ` Marek Polacek
2019-06-12  3:46         ` Jason Merrill
2019-06-14 20:54           ` Marek Polacek
2019-06-21 20:47             ` Jason Merrill
2019-06-21 21:30               ` Marek Polacek
2019-06-22  0:28                 ` Jason Merrill

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190104144446.GD21364@redhat.com \
    --to=polacek@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).