This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [C++ PATCH v2] PR c++/92907 - noexcept does not consider "const" in member functions.
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
--
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA