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 @@ -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))); +}; base-commit: 8e0efc10335bf9bb447f2188254609dcfad7de8a -- Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA