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 <pola...@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 <pola...@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 -- Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA