diff mbox series

[C++] PR c++/92907 - noexcept does not consider "const" in member functions.

Message ID 20200122020826.1140392-1-polacek@redhat.com
State New
Headers show
Series [C++] PR c++/92907 - noexcept does not consider "const" in member functions. | expand

Commit Message

Marek Polacek Jan. 22, 2020, 2:08 a.m. UTC
Here the problem is that if the noexcept specifier is used in the context
of a const member function, const is not considered for the member variables,
leading to a bogus error.  g's const makes its 'this' const, so the first
overload of f should be selected.

In cp_parser_noexcept_specification_opt we inject 'this', but always
unqualified:
25737           if (current_class_type)
25738             inject_this_parameter (current_class_type, TYPE_UNQUALIFIED);
so we need to pass the function's qualifiers down here.  In
cp_parser_direct_declarator it's easy: use the just parsed cv_quals, in
cp_parser_late_noexcept_specifier look at the 'this' parameter to figure it
out.

Bootstrapped/regtested on x86_64-linux, ok for trunk?  Not planning to
backport it to 9, this is not really a regression.

2020-01-21  Marek Polacek  <polacek@redhat.com>

	PR c++/92907 - noexcept does not consider "const" in member functions.
	* parser.c (cp_parser_lambda_declarator_opt): Pass TYPE_UNQUALIFIED
	down to cp_parser_exception_specification_opt.
	(cp_parser_direct_declarator): Pass the function qualifiers to
	cp_parser_exception_specification_opt.
	(cp_parser_class_specifier_1): Pass the function declaration to
	cp_parser_late_noexcept_specifier.
	(cp_parser_late_noexcept_specifier): Add a tree parameter.  Use it to
	pass the qualifiers of the function to
	cp_parser_noexcept_specification_opt.
	(cp_parser_noexcept_specification_opt): New cp_cv_quals parameter.
	Use it in inject_this_parameter.
	(cp_parser_exception_specification_opt): New cp_cv_quals parameter.
	Use it.
	(cp_parser_transaction): Pass TYPE_UNQUALIFIED to
	cp_parser_noexcept_specification_opt.
	(cp_parser_transaction_expression): Likewise.

	* g++.dg/cpp0x/noexcept56.C: New test.
---
 gcc/cp/parser.c                         | 53 +++++++++++++++++--------
 gcc/testsuite/g++.dg/cpp0x/noexcept56.C | 10 +++++
 2 files changed, 46 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept56.C


base-commit: 8e0efc10335bf9bb447f2188254609dcfad7de8a

Comments

Jason Merrill Jan. 22, 2020, 3:40 a.m. UTC | #1
On 1/21/20 9:08 PM, Marek Polacek wrote:
> Here the problem is that if the noexcept specifier is used in the context
> of a const member function, const is not considered for the member variables,
> leading to a bogus error.  g's const makes its 'this' const, so the first
> overload of f should be selected.
> 
> In cp_parser_noexcept_specification_opt we inject 'this', but always
> unqualified:
> 25737           if (current_class_type)
> 25738             inject_this_parameter (current_class_type, TYPE_UNQUALIFIED);
> so we need to pass the function's qualifiers down here.  In
> cp_parser_direct_declarator it's easy: use the just parsed cv_quals, in
> cp_parser_late_noexcept_specifier look at the 'this' parameter to figure it
> out.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?  Not planning to
> backport it to 9, this is not really a regression.
> 
> 2020-01-21  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/92907 - noexcept does not consider "const" in member functions.
> 	* parser.c (cp_parser_lambda_declarator_opt): Pass TYPE_UNQUALIFIED
> 	down to cp_parser_exception_specification_opt.
> 	(cp_parser_direct_declarator): Pass the function qualifiers to
> 	cp_parser_exception_specification_opt.
> 	(cp_parser_class_specifier_1): Pass the function declaration to
> 	cp_parser_late_noexcept_specifier.
> 	(cp_parser_late_noexcept_specifier): Add a tree parameter.  Use it to
> 	pass the qualifiers of the function to
> 	cp_parser_noexcept_specification_opt.
> 	(cp_parser_noexcept_specification_opt): New cp_cv_quals parameter.
> 	Use it in inject_this_parameter.
> 	(cp_parser_exception_specification_opt): New cp_cv_quals parameter.
> 	Use it.
> 	(cp_parser_transaction): Pass TYPE_UNQUALIFIED to
> 	cp_parser_noexcept_specification_opt.
> 	(cp_parser_transaction_expression): Likewise.
> 
> 	* g++.dg/cpp0x/noexcept56.C: New test.
> ---
>   gcc/cp/parser.c                         | 53 +++++++++++++++++--------
>   gcc/testsuite/g++.dg/cpp0x/noexcept56.C | 10 +++++
>   2 files changed, 46 insertions(+), 17 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept56.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index caafbefda8e..e3566f9bd4d 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -11008,7 +11008,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
>   
>         /* Parse optional exception specification.  */
>         exception_spec
> -	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
> +	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE,
> +						 TYPE_UNQUALIFIED);

This seems wrong; a lambda op() is const unless explicitly 'mutable'.  A 
bit further down we have

>     quals = (LAMBDA_EXPR_MUTABLE_P (lambda_expr)
>              ? TYPE_UNQUALIFIED : TYPE_QUAL_CONST);

You can probably move that up?

Jason
Marek Polacek Jan. 22, 2020, 4:33 p.m. UTC | #2
On Tue, Jan 21, 2020 at 10:40:09PM -0500, Jason Merrill wrote:
> On 1/21/20 9:08 PM, Marek Polacek wrote:
> > Here the problem is that if the noexcept specifier is used in the context
> > of a const member function, const is not considered for the member variables,
> > leading to a bogus error.  g's const makes its 'this' const, so the first
> > overload of f should be selected.
> > 
> > In cp_parser_noexcept_specification_opt we inject 'this', but always
> > unqualified:
> > 25737           if (current_class_type)
> > 25738             inject_this_parameter (current_class_type, TYPE_UNQUALIFIED);
> > so we need to pass the function's qualifiers down here.  In
> > cp_parser_direct_declarator it's easy: use the just parsed cv_quals, in
> > cp_parser_late_noexcept_specifier look at the 'this' parameter to figure it
> > out.
> > 
> > Bootstrapped/regtested on x86_64-linux, ok for trunk?  Not planning to
> > backport it to 9, this is not really a regression.
> > 
> > 2020-01-21  Marek Polacek  <polacek@redhat.com>
> > 
> > 	PR c++/92907 - noexcept does not consider "const" in member functions.
> > 	* parser.c (cp_parser_lambda_declarator_opt): Pass TYPE_UNQUALIFIED
> > 	down to cp_parser_exception_specification_opt.
> > 	(cp_parser_direct_declarator): Pass the function qualifiers to
> > 	cp_parser_exception_specification_opt.
> > 	(cp_parser_class_specifier_1): Pass the function declaration to
> > 	cp_parser_late_noexcept_specifier.
> > 	(cp_parser_late_noexcept_specifier): Add a tree parameter.  Use it to
> > 	pass the qualifiers of the function to
> > 	cp_parser_noexcept_specification_opt.
> > 	(cp_parser_noexcept_specification_opt): New cp_cv_quals parameter.
> > 	Use it in inject_this_parameter.
> > 	(cp_parser_exception_specification_opt): New cp_cv_quals parameter.
> > 	Use it.
> > 	(cp_parser_transaction): Pass TYPE_UNQUALIFIED to
> > 	cp_parser_noexcept_specification_opt.
> > 	(cp_parser_transaction_expression): Likewise.
> > 
> > 	* g++.dg/cpp0x/noexcept56.C: New test.
> > ---
> >   gcc/cp/parser.c                         | 53 +++++++++++++++++--------
> >   gcc/testsuite/g++.dg/cpp0x/noexcept56.C | 10 +++++
> >   2 files changed, 46 insertions(+), 17 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept56.C
> > 
> > diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> > index caafbefda8e..e3566f9bd4d 100644
> > --- a/gcc/cp/parser.c
> > +++ b/gcc/cp/parser.c
> > @@ -11008,7 +11008,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
> >         /* Parse optional exception specification.  */
> >         exception_spec
> > -	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
> > +	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE,
> > +						 TYPE_UNQUALIFIED);
> 
> This seems wrong; a lambda op() is const unless explicitly 'mutable'.  A bit
> further down we have

Indeed, I didn't change it for lambdas at all.

> >     quals = (LAMBDA_EXPR_MUTABLE_P (lambda_expr)
> >              ? TYPE_UNQUALIFIED : TYPE_QUAL_CONST);
> 
> You can probably move that up?

Here's what I did.  Unfortunately it doesn't fix PR79620.

-- >8 --
Here the problem is that if the noexcept specifier is used in the context
of a const member function, const is not considered for the member variables,
leading to a bogus error.  g's const makes its 'this' const, so the first
overload of f should be selected.

In cp_parser_noexcept_specification_opt we inject 'this', but always
unqualified:
25737           if (current_class_type)
25738             inject_this_parameter (current_class_type, TYPE_UNQUALIFIED);
so we need to pass the function's qualifiers down here.  In
cp_parser_direct_declarator it's easy: use the just parsed cv_quals, in
cp_parser_late_noexcept_specifier look at the 'this' parameter to figure it
out.

Bootstrapped/regtested on x86_64-linux, ok for trunk?  Not planning to
backport it to 9, this is not really a regression.

2020-01-22  Marek Polacek  <polacek@redhat.com>

	PR c++/92907 - noexcept does not consider "const" in member functions.
	* parser.c (cp_parser_lambda_declarator_opt): Pass the proper
	qualifiers to cp_parser_exception_specification_opt.
	(cp_parser_direct_declarator): Pass the function qualifiers to
	cp_parser_exception_specification_opt.
	(cp_parser_class_specifier_1): Pass the function declaration to
	cp_parser_late_noexcept_specifier.
	(cp_parser_late_noexcept_specifier): Add a tree parameter.  Use it to
	pass the qualifiers of the function to
	cp_parser_noexcept_specification_opt.
	(cp_parser_noexcept_specification_opt): New cp_cv_quals parameter.
	Use it in inject_this_parameter.
	(cp_parser_exception_specification_opt): New cp_cv_quals parameter.
	Use it.
	(cp_parser_transaction): Pass TYPE_UNQUALIFIED to
	cp_parser_noexcept_specification_opt.
	(cp_parser_transaction_expression): Likewise.

	* g++.dg/cpp0x/noexcept56.C: New test.
---
 gcc/cp/parser.c                         | 59 ++++++++++++++++---------
 gcc/testsuite/g++.dg/cpp0x/noexcept56.C | 10 +++++
 2 files changed, 49 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept56.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index caafbefda8e..5bdd9d75b48 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -246,7 +246,7 @@ static void cp_lexer_stop_debugging
 static cp_token_cache *cp_token_cache_new
   (cp_token *, cp_token *);
 static tree cp_parser_late_noexcept_specifier
-  (cp_parser *, tree);
+  (cp_parser *, tree, tree);
 static void noexcept_override_late_checks
   (tree, tree);
 
@@ -2388,11 +2388,11 @@ 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_flags);
+  (cp_parser *, cp_parser_flags, cp_cv_quals);
 static tree cp_parser_type_id_list
   (cp_parser *);
 static tree cp_parser_noexcept_specification_opt
-  (cp_parser *, cp_parser_flags, bool, bool *, bool);
+  (cp_parser *, cp_parser_flags, bool, bool *, bool, cp_cv_quals);
 
 /* GNU Extensions */
 
@@ -10908,6 +10908,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
   tree trailing_requires_clause = NULL_TREE;
   cp_decl_specifier_seq lambda_specs;
   clear_decl_specs (&lambda_specs);
+  /* A lambda op() is const unless explicitly 'mutable'.  */
+  cp_cv_quals quals = TYPE_QUAL_CONST;
 
   /* The template-parameter-list is optional, but must begin with
      an opening angle if present.  */
@@ -10999,6 +11001,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       if (lambda_specs.storage_class == sc_mutable)
 	{
 	  LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
+	  quals = TYPE_UNQUALIFIED;
 	  if (lambda_specs.conflicting_specifiers_p)
 	    error_at (lambda_specs.locations[ds_storage_class],
 		      "duplicate %<mutable%>");
@@ -11008,7 +11011,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 
       /* Parse optional exception specification.  */
       exception_spec
-	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
+	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE,
+						 quals);
 
       std_attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -11041,7 +11045,6 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
     cp_decl_specifier_seq return_type_specs;
     cp_declarator* declarator;
     tree fco;
-    int quals;
     void *p;
 
     clear_decl_specs (&return_type_specs);
@@ -11066,8 +11069,6 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
     declarator = make_id_declarator (NULL_TREE, call_op_identifier, sfk_none,
 				     LAMBDA_EXPR_LOCATION (lambda_expr));
 
-    quals = (LAMBDA_EXPR_MUTABLE_P (lambda_expr)
-	     ? TYPE_UNQUALIFIED : TYPE_QUAL_CONST);
     declarator = make_call_declarator (declarator, param_list, quals,
 				       VIRT_SPEC_UNSPECIFIED,
                                        REF_QUAL_NONE,
@@ -21126,7 +21127,9 @@ 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, flags);
+		    = cp_parser_exception_specification_opt (parser,
+							     flags,
+							     cv_quals);
 
 		  attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -23984,7 +23987,7 @@ cp_parser_class_specifier_1 (cp_parser* parser)
 	    parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
 
 	  /* Now we can parse the noexcept-specifier.  */
-	  spec = cp_parser_late_noexcept_specifier (parser, spec);
+	  spec = cp_parser_late_noexcept_specifier (parser, spec, decl);
 
 	  if (spec != error_mark_node)
 	    TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
@@ -25611,10 +25614,12 @@ cp_parser_save_noexcept (cp_parser *parser)
 
 /* 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.  */
+   we saved for later; parse it now.  DECL is the declaration of the
+   member function.  */
 
 static tree
-cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg,
+				   tree decl)
 {
   /* Make sure we've gotten something that hasn't been parsed yet.  */
   gcc_assert (TREE_CODE (default_arg) == DEFERRED_PARSE);
@@ -25626,13 +25631,20 @@ cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
   cp_token_cache *tokens = DEFPARSE_TOKENS (default_arg);
   cp_parser_push_lexer_for_tokens (parser, tokens);
 
+  /* We need to know if this member function was declared `const'.  Look
+     at the this parameter to figure that out.  */
+  cp_cv_quals quals;
+  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
+    quals = cp_type_quals (class_of_this_parm (TREE_TYPE (decl)));
+  else
+    quals = TYPE_UNQUALIFIED;
   /* Parse the cached noexcept-specifier.  */
   tree parsed_arg
     = cp_parser_noexcept_specification_opt (parser,
 					    CP_PARSER_FLAGS_NONE,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false);
+					    /*return_cond=*/false, quals);
 
   /* Revert to the main lexer.  */
   cp_parser_pop_lexer (parser);
@@ -25682,14 +25694,16 @@ noexcept_override_late_checks (tree type, tree fndecl)
    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.  The parser flags
-   FLAGS is used to control parsing.  */
+   FLAGS is used to control parsing.  QUALS are qualifiers indicating whether
+   the (member) function is `const'.  */
 
 static tree
 cp_parser_noexcept_specification_opt (cp_parser* parser,
 				      cp_parser_flags flags,
 				      bool require_constexpr,
 				      bool* consumed_expr,
-				      bool return_cond)
+				      bool return_cond,
+				      cp_cv_quals quals)
 {
   cp_token *token;
   const char *saved_message;
@@ -25735,7 +25749,7 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
 	  tree save_ccr = current_class_ref;
 
 	  if (current_class_type)
-	    inject_this_parameter (current_class_type, TYPE_UNQUALIFIED);
+	    inject_this_parameter (current_class_type, quals);
 
 	  if (require_constexpr)
 	    {
@@ -25795,10 +25809,13 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
 
    Returns a TREE_LIST representing the exception-specification.  The
    TREE_VALUE of each node is a type.  The parser flags FLAGS is used to
-   control parsing.  */
+   control parsing.  QUALS are qualifiers indicating whether the (member)
+   function is `const'.  */
 
 static tree
-cp_parser_exception_specification_opt (cp_parser* parser, cp_parser_flags flags)
+cp_parser_exception_specification_opt (cp_parser* parser,
+				       cp_parser_flags flags,
+				       cp_cv_quals quals)
 {
   cp_token *token;
   tree type_id_list;
@@ -25812,7 +25829,7 @@ cp_parser_exception_specification_opt (cp_parser* parser, cp_parser_flags flags)
     = cp_parser_noexcept_specification_opt (parser, flags,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false);
+					    /*return_cond=*/false, quals);
   if (type_id_list != NULL_TREE)
     return type_id_list;
 
@@ -42964,7 +42981,8 @@ cp_parser_transaction (cp_parser *parser, cp_token *token)
 						 CP_PARSER_FLAGS_NONE,
 						 /*require_constexpr=*/true,
 						 /*consumed_expr=*/NULL,
-						 /*return_cond=*/true);
+						 /*return_cond=*/true,
+						 TYPE_UNQUALIFIED);
 
   /* Keep track if we're in the lexical scope of an outer transaction.  */
   new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -43028,7 +43046,8 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
 					       CP_PARSER_FLAGS_NONE,
 					       /*require_constexpr=*/false,
 					       &noex_expr,
-					       /*return_cond=*/true);
+					       /*return_cond=*/true,
+					       TYPE_UNQUALIFIED);
 
   if (!noex || !noex_expr
       || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept56.C b/gcc/testsuite/g++.dg/cpp0x/noexcept56.C
new file mode 100644
index 00000000000..8eea6b91f7e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept56.C
@@ -0,0 +1,10 @@
+// PR c++/92907 - noexcept does not consider "const" in member functions.
+// { dg-do compile { target c++11 } }
+
+void f(const int&);
+void f(int&) = delete;
+
+struct A {
+  int i;
+  void g() const noexcept(noexcept(f(i)));
+};

base-commit: 7c46e71d016c86971ac26c6fa38d76482859f296
Jason Merrill Jan. 22, 2020, 5:10 p.m. UTC | #3
On 1/22/20 11:33 AM, Marek Polacek wrote:
> Here's what I did.  Unfortunately it doesn't fix PR79620.

No, I wouldn't expect it to, that looks like more of an issue with how 
is_lambda_ignored_entity skips captures in unevaluated context.

> +  cp_cv_quals quals;
> +  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
> +    quals = cp_type_quals (class_of_this_parm (TREE_TYPE (decl)));
> +  else
> +    quals = TYPE_UNQUALIFIED;

This could be

cp_cv_quals quals = type_memfn_quals (TREE_TYPE (decl));

OK with that change.

Jason
Marek Polacek Jan. 22, 2020, 5:26 p.m. UTC | #4
On Wed, Jan 22, 2020 at 12:10:12PM -0500, Jason Merrill wrote:
> On 1/22/20 11:33 AM, Marek Polacek wrote:
> > Here's what I did.  Unfortunately it doesn't fix PR79620.
> 
> No, I wouldn't expect it to, that looks like more of an issue with how
> is_lambda_ignored_entity skips captures in unevaluated context.
> 
> > +  cp_cv_quals quals;
> > +  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
> > +    quals = cp_type_quals (class_of_this_parm (TREE_TYPE (decl)));
> > +  else
> > +    quals = TYPE_UNQUALIFIED;
> 
> This could be
> 
> cp_cv_quals quals = type_memfn_quals (TREE_TYPE (decl));
> 
> OK with that change.

Awesome, thanks!  Here's what I'm going to commit, er, push:

Here the problem is that if the noexcept specifier is used in the context
of a const member function, const is not considered for the member variables,
leading to a bogus error.  g's const makes its 'this' const, so the first
overload of f should be selected.

In cp_parser_noexcept_specification_opt we inject 'this', but always
unqualified:
25737           if (current_class_type)
25738             inject_this_parameter (current_class_type, TYPE_UNQUALIFIED);
so we need to pass the function's qualifiers down here.  In
cp_parser_direct_declarator it's easy: use the just parsed cv_quals, in
cp_parser_late_noexcept_specifier look at the 'this' parameter to figure it
out.

2020-01-22  Marek Polacek  <polacek@redhat.com>

	PR c++/92907 - noexcept does not consider "const" in member functions.
	* parser.c (cp_parser_lambda_declarator_opt): Pass the proper
	qualifiers to cp_parser_exception_specification_opt.
	(cp_parser_direct_declarator): Pass the function qualifiers to
	cp_parser_exception_specification_opt.
	(cp_parser_class_specifier_1): Pass the function declaration to
	cp_parser_late_noexcept_specifier.
	(cp_parser_late_noexcept_specifier): Add a tree parameter.  Use it to
	pass the qualifiers of the function to
	cp_parser_noexcept_specification_opt.
	(cp_parser_noexcept_specification_opt): New cp_cv_quals parameter.
	Use it in inject_this_parameter.
	(cp_parser_exception_specification_opt): New cp_cv_quals parameter.
	Use it.
	(cp_parser_transaction): Pass TYPE_UNQUALIFIED to
	cp_parser_noexcept_specification_opt.
	(cp_parser_transaction_expression): Likewise.

	* g++.dg/cpp0x/noexcept56.C: New test.
---
 gcc/cp/parser.c                         | 55 ++++++++++++++++---------
 gcc/testsuite/g++.dg/cpp0x/noexcept56.C | 10 +++++
 2 files changed, 45 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept56.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index ea32df92f9c..dc07dc55d9c 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -246,7 +246,7 @@ static void cp_lexer_stop_debugging
 static cp_token_cache *cp_token_cache_new
   (cp_token *, cp_token *);
 static tree cp_parser_late_noexcept_specifier
-  (cp_parser *, tree);
+  (cp_parser *, tree, tree);
 static void noexcept_override_late_checks
   (tree, tree);
 
@@ -2388,11 +2388,11 @@ 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_flags);
+  (cp_parser *, cp_parser_flags, cp_cv_quals);
 static tree cp_parser_type_id_list
   (cp_parser *);
 static tree cp_parser_noexcept_specification_opt
-  (cp_parser *, cp_parser_flags, bool, bool *, bool);
+  (cp_parser *, cp_parser_flags, bool, bool *, bool, cp_cv_quals);
 
 /* GNU Extensions */
 
@@ -10908,6 +10908,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
   tree trailing_requires_clause = NULL_TREE;
   cp_decl_specifier_seq lambda_specs;
   clear_decl_specs (&lambda_specs);
+  /* A lambda op() is const unless explicitly 'mutable'.  */
+  cp_cv_quals quals = TYPE_QUAL_CONST;
 
   /* The template-parameter-list is optional, but must begin with
      an opening angle if present.  */
@@ -10999,6 +11001,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       if (lambda_specs.storage_class == sc_mutable)
 	{
 	  LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
+	  quals = TYPE_UNQUALIFIED;
 	  if (lambda_specs.conflicting_specifiers_p)
 	    error_at (lambda_specs.locations[ds_storage_class],
 		      "duplicate %<mutable%>");
@@ -11008,7 +11011,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 
       /* Parse optional exception specification.  */
       exception_spec
-	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
+	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE,
+						 quals);
 
       std_attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -11041,7 +11045,6 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
     cp_decl_specifier_seq return_type_specs;
     cp_declarator* declarator;
     tree fco;
-    int quals;
     void *p;
 
     clear_decl_specs (&return_type_specs);
@@ -11066,8 +11069,6 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
     declarator = make_id_declarator (NULL_TREE, call_op_identifier, sfk_none,
 				     LAMBDA_EXPR_LOCATION (lambda_expr));
 
-    quals = (LAMBDA_EXPR_MUTABLE_P (lambda_expr)
-	     ? TYPE_UNQUALIFIED : TYPE_QUAL_CONST);
     declarator = make_call_declarator (declarator, param_list, quals,
 				       VIRT_SPEC_UNSPECIFIED,
                                        REF_QUAL_NONE,
@@ -21127,7 +21128,9 @@ 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, flags);
+		    = cp_parser_exception_specification_opt (parser,
+							     flags,
+							     cv_quals);
 
 		  attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -23985,7 +23988,7 @@ cp_parser_class_specifier_1 (cp_parser* parser)
 	    parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
 
 	  /* Now we can parse the noexcept-specifier.  */
-	  spec = cp_parser_late_noexcept_specifier (parser, spec);
+	  spec = cp_parser_late_noexcept_specifier (parser, spec, decl);
 
 	  if (spec != error_mark_node)
 	    TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
@@ -25612,10 +25615,12 @@ cp_parser_save_noexcept (cp_parser *parser)
 
 /* 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.  */
+   we saved for later; parse it now.  DECL is the declaration of the
+   member function.  */
 
 static tree
-cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg,
+				   tree decl)
 {
   /* Make sure we've gotten something that hasn't been parsed yet.  */
   gcc_assert (TREE_CODE (default_arg) == DEFERRED_PARSE);
@@ -25627,13 +25632,16 @@ cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
   cp_token_cache *tokens = DEFPARSE_TOKENS (default_arg);
   cp_parser_push_lexer_for_tokens (parser, tokens);
 
+  /* We need to know if this member function was declared `const'.  Look
+     at the this parameter to figure that out.  */
+  cp_cv_quals quals = type_memfn_quals (TREE_TYPE (decl));
   /* Parse the cached noexcept-specifier.  */
   tree parsed_arg
     = cp_parser_noexcept_specification_opt (parser,
 					    CP_PARSER_FLAGS_NONE,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false);
+					    /*return_cond=*/false, quals);
 
   /* Revert to the main lexer.  */
   cp_parser_pop_lexer (parser);
@@ -25683,14 +25691,16 @@ noexcept_override_late_checks (tree type, tree fndecl)
    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.  The parser flags
-   FLAGS is used to control parsing.  */
+   FLAGS is used to control parsing.  QUALS are qualifiers indicating whether
+   the (member) function is `const'.  */
 
 static tree
 cp_parser_noexcept_specification_opt (cp_parser* parser,
 				      cp_parser_flags flags,
 				      bool require_constexpr,
 				      bool* consumed_expr,
-				      bool return_cond)
+				      bool return_cond,
+				      cp_cv_quals quals)
 {
   cp_token *token;
   const char *saved_message;
@@ -25736,7 +25746,7 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
 	  tree save_ccr = current_class_ref;
 
 	  if (current_class_type)
-	    inject_this_parameter (current_class_type, TYPE_UNQUALIFIED);
+	    inject_this_parameter (current_class_type, quals);
 
 	  if (require_constexpr)
 	    {
@@ -25796,10 +25806,13 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
 
    Returns a TREE_LIST representing the exception-specification.  The
    TREE_VALUE of each node is a type.  The parser flags FLAGS is used to
-   control parsing.  */
+   control parsing.  QUALS are qualifiers indicating whether the (member)
+   function is `const'.  */
 
 static tree
-cp_parser_exception_specification_opt (cp_parser* parser, cp_parser_flags flags)
+cp_parser_exception_specification_opt (cp_parser* parser,
+				       cp_parser_flags flags,
+				       cp_cv_quals quals)
 {
   cp_token *token;
   tree type_id_list;
@@ -25813,7 +25826,7 @@ cp_parser_exception_specification_opt (cp_parser* parser, cp_parser_flags flags)
     = cp_parser_noexcept_specification_opt (parser, flags,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false);
+					    /*return_cond=*/false, quals);
   if (type_id_list != NULL_TREE)
     return type_id_list;
 
@@ -42965,7 +42978,8 @@ cp_parser_transaction (cp_parser *parser, cp_token *token)
 						 CP_PARSER_FLAGS_NONE,
 						 /*require_constexpr=*/true,
 						 /*consumed_expr=*/NULL,
-						 /*return_cond=*/true);
+						 /*return_cond=*/true,
+						 TYPE_UNQUALIFIED);
 
   /* Keep track if we're in the lexical scope of an outer transaction.  */
   new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -43029,7 +43043,8 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
 					       CP_PARSER_FLAGS_NONE,
 					       /*require_constexpr=*/false,
 					       &noex_expr,
-					       /*return_cond=*/true);
+					       /*return_cond=*/true,
+					       TYPE_UNQUALIFIED);
 
   if (!noex || !noex_expr
       || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept56.C b/gcc/testsuite/g++.dg/cpp0x/noexcept56.C
new file mode 100644
index 00000000000..8eea6b91f7e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept56.C
@@ -0,0 +1,10 @@
+// PR c++/92907 - noexcept does not consider "const" in member functions.
+// { dg-do compile { target c++11 } }
+
+void f(const int&);
+void f(int&) = delete;
+
+struct A {
+  int i;
+  void g() const noexcept(noexcept(f(i)));
+};

base-commit: 15ed55eabb0cf8a2974b8025a9f46c9e58960811
diff mbox series

Patch

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index caafbefda8e..e3566f9bd4d 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -246,7 +246,7 @@  static void cp_lexer_stop_debugging
 static cp_token_cache *cp_token_cache_new
   (cp_token *, cp_token *);
 static tree cp_parser_late_noexcept_specifier
-  (cp_parser *, tree);
+  (cp_parser *, tree, tree);
 static void noexcept_override_late_checks
   (tree, tree);
 
@@ -2388,11 +2388,11 @@  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_flags);
+  (cp_parser *, cp_parser_flags, cp_cv_quals);
 static tree cp_parser_type_id_list
   (cp_parser *);
 static tree cp_parser_noexcept_specification_opt
-  (cp_parser *, cp_parser_flags, bool, bool *, bool);
+  (cp_parser *, cp_parser_flags, bool, bool *, bool, cp_cv_quals);
 
 /* GNU Extensions */
 
@@ -11008,7 +11008,8 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 
       /* Parse optional exception specification.  */
       exception_spec
-	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
+	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE,
+						 TYPE_UNQUALIFIED);
 
       std_attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -21126,7 +21127,9 @@  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, flags);
+		    = cp_parser_exception_specification_opt (parser,
+							     flags,
+							     cv_quals);
 
 		  attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -23984,7 +23987,7 @@  cp_parser_class_specifier_1 (cp_parser* parser)
 	    parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
 
 	  /* Now we can parse the noexcept-specifier.  */
-	  spec = cp_parser_late_noexcept_specifier (parser, spec);
+	  spec = cp_parser_late_noexcept_specifier (parser, spec, decl);
 
 	  if (spec != error_mark_node)
 	    TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
@@ -25611,10 +25614,12 @@  cp_parser_save_noexcept (cp_parser *parser)
 
 /* 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.  */
+   we saved for later; parse it now.  DECL is the declaration of the
+   member function.  */
 
 static tree
-cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg,
+				   tree decl)
 {
   /* Make sure we've gotten something that hasn't been parsed yet.  */
   gcc_assert (TREE_CODE (default_arg) == DEFERRED_PARSE);
@@ -25626,13 +25631,20 @@  cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
   cp_token_cache *tokens = DEFPARSE_TOKENS (default_arg);
   cp_parser_push_lexer_for_tokens (parser, tokens);
 
+  /* We need to know if this member function was declared `const'.  Look
+     at the this parameter to figure that out.  */
+  cp_cv_quals quals;
+  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
+    quals = cp_type_quals (class_of_this_parm (TREE_TYPE (decl)));
+  else
+    quals = TYPE_UNQUALIFIED;
   /* Parse the cached noexcept-specifier.  */
   tree parsed_arg
     = cp_parser_noexcept_specification_opt (parser,
 					    CP_PARSER_FLAGS_NONE,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false);
+					    /*return_cond=*/false, quals);
 
   /* Revert to the main lexer.  */
   cp_parser_pop_lexer (parser);
@@ -25682,14 +25694,16 @@  noexcept_override_late_checks (tree type, tree fndecl)
    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.  The parser flags
-   FLAGS is used to control parsing.  */
+   FLAGS is used to control parsing.  QUALS are qualifiers indicating whether
+   the (member) function is `const'.  */
 
 static tree
 cp_parser_noexcept_specification_opt (cp_parser* parser,
 				      cp_parser_flags flags,
 				      bool require_constexpr,
 				      bool* consumed_expr,
-				      bool return_cond)
+				      bool return_cond,
+				      cp_cv_quals quals)
 {
   cp_token *token;
   const char *saved_message;
@@ -25735,7 +25749,7 @@  cp_parser_noexcept_specification_opt (cp_parser* parser,
 	  tree save_ccr = current_class_ref;
 
 	  if (current_class_type)
-	    inject_this_parameter (current_class_type, TYPE_UNQUALIFIED);
+	    inject_this_parameter (current_class_type, quals);
 
 	  if (require_constexpr)
 	    {
@@ -25795,10 +25809,13 @@  cp_parser_noexcept_specification_opt (cp_parser* parser,
 
    Returns a TREE_LIST representing the exception-specification.  The
    TREE_VALUE of each node is a type.  The parser flags FLAGS is used to
-   control parsing.  */
+   control parsing.  QUALS are qualifiers indicating whether the (member)
+   function is `const'.  */
 
 static tree
-cp_parser_exception_specification_opt (cp_parser* parser, cp_parser_flags flags)
+cp_parser_exception_specification_opt (cp_parser* parser,
+				       cp_parser_flags flags,
+				       cp_cv_quals quals)
 {
   cp_token *token;
   tree type_id_list;
@@ -25812,7 +25829,7 @@  cp_parser_exception_specification_opt (cp_parser* parser, cp_parser_flags flags)
     = cp_parser_noexcept_specification_opt (parser, flags,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false);
+					    /*return_cond=*/false, quals);
   if (type_id_list != NULL_TREE)
     return type_id_list;
 
@@ -42964,7 +42981,8 @@  cp_parser_transaction (cp_parser *parser, cp_token *token)
 						 CP_PARSER_FLAGS_NONE,
 						 /*require_constexpr=*/true,
 						 /*consumed_expr=*/NULL,
-						 /*return_cond=*/true);
+						 /*return_cond=*/true,
+						 TYPE_UNQUALIFIED);
 
   /* Keep track if we're in the lexical scope of an outer transaction.  */
   new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -43028,7 +43046,8 @@  cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
 					       CP_PARSER_FLAGS_NONE,
 					       /*require_constexpr=*/false,
 					       &noex_expr,
-					       /*return_cond=*/true);
+					       /*return_cond=*/true,
+					       TYPE_UNQUALIFIED);
 
   if (!noex || !noex_expr
       || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept56.C b/gcc/testsuite/g++.dg/cpp0x/noexcept56.C
new file mode 100644
index 00000000000..8eea6b91f7e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept56.C
@@ -0,0 +1,10 @@ 
+// PR c++/92907 - noexcept does not consider "const" in member functions.
+// { dg-do compile { target c++11 } }
+
+void f(const int&);
+void f(int&) = delete;
+
+struct A {
+  int i;
+  void g() const noexcept(noexcept(f(i)));
+};