From: Marek Polacek Date: Mon, 11 May 2026 21:19:42 +0000 (-0400) Subject: c++: deferred parsing of default arguments [PR50479] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5fd9f0f77f2bdb9a2843bb9b9b55ab6bd2cbd5c8;p=thirdparty%2Fgcc.git c++: deferred parsing of default arguments [PR50479] In void fn (int i = sizeof (i)) {} the i in sizeof should refer to the parameter ([basic.scope.pdecl]) and since the second i is not evaluated, the code is valid per [dcl.fct.default]/9: A parameter shall not appear as a potentially evaluated expression in a default argument. This patch fixes this by moving the grokdeclarator call from _parameter_declaration_list to _parameter_declaration and maybe calling pushdecl before parsing the default argument. PR c++/50479 PR c++/62244 gcc/cp/ChangeLog: * parser.cc (cp_parser_parameter_declaration_list): Move the grokdeclarator call and setting DECL_SOURCE_LOCATION to... (cp_parser_parameter_declaration): ...here. New tree parameter. Set it. Call pushdecl for a named decl with a default argument. gcc/testsuite/ChangeLog: * g++.dg/reflect/parm1.C: Uncomment code. * g++.dg/parse/defarg22.C: New test. * g++.dg/parse/defarg23.C: New test. * g++.dg/parse/defarg24.C: New test. * g++.dg/parse/defarg25.C: New test. * g++.dg/parse/defarg26.C: New test. Reviewed-by: Jason Merrill --- diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index d128de771b5..c08f4cee920 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -2785,7 +2785,7 @@ static tree cp_parser_parameter_declaration_clause static tree cp_parser_parameter_declaration_list (cp_parser *, cp_parser_flags, auto_vec *); static cp_parameter_declarator *cp_parser_parameter_declaration - (cp_parser *, cp_parser_flags, bool, bool *); + (cp_parser *, cp_parser_flags, bool, bool *, tree * = nullptr); static tree cp_parser_default_argument (cp_parser *, bool); static void cp_parser_function_body @@ -28301,24 +28301,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser, parameter = cp_parser_parameter_declaration (parser, flags, /*template_parm_p=*/false, - &parenthesized_p); - - /* We don't know yet if the enclosing context is unavailable or deprecated, - so wait and deal with it in grokparms if appropriate. */ - deprecated_state = UNAVAILABLE_DEPRECATED_SUPPRESS; - - if (parameter && !cp_parser_error_occurred (parser)) - { - decl = grokdeclarator (parameter->declarator, - ¶meter->decl_specifiers, - PARM, - parameter->default_argument != NULL_TREE, - ¶meter->decl_specifiers.attributes); - if (decl != error_mark_node && parameter->loc != UNKNOWN_LOCATION) - DECL_SOURCE_LOCATION (decl) = parameter->loc; - } - - deprecated_state = DEPRECATED_NORMAL; + &parenthesized_p, &decl); /* If a parse error occurred parsing the parameter declaration, then the entire parameter-declaration-list is erroneous. */ @@ -28361,7 +28344,10 @@ cp_parser_parameter_declaration_list (cp_parser* parser, return false; }()) pending_decls->safe_push (decl); - else + /* If we saw a default argument, we've already pushed this decl. + (An ill-formed default argument should have been parsed to + error_mark_node.) */ + else if (!parameter->default_argument) decl = pushdecl (decl); } @@ -28474,6 +28460,10 @@ cp_parser_parameter_declaration_list (cp_parser* parser, token encountered during the parsing of the assignment-expression is not interpreted as a greater-than operator.) + If DECLP is non-null, we are called from cp_parser_parameter_declaration_list + and will perform grokdeclarator here; DECLP will be set to the result. If + we see a default argument, we also call pushdecl; see below for rationale. + Returns a representation of the parameter, or NULL if an error occurs. If PARENTHESIZED_P is non-NULL, *PARENTHESIZED_P is set to true iff the declarator is of the form "(p)". */ @@ -28482,7 +28472,8 @@ static cp_parameter_declarator * cp_parser_parameter_declaration (cp_parser *parser, cp_parser_flags flags, bool template_parm_p, - bool *parenthesized_p) + bool *parenthesized_p, + tree *declp/*=nullptr*/) { int declares_class_or_enum; cp_decl_specifier_seq decl_specifiers; @@ -28697,13 +28688,37 @@ cp_parser_parameter_declaration (cp_parser *parser, decl_specifiers.locations[ds_this] = 0; } + const bool has_defarg_p = cp_lexer_next_token_is (parser->lexer, CPP_EQ); + + /* We don't know yet if the enclosing context is unavailable or deprecated, + so wait and deal with it in grokparms if appropriate. */ + deprecated_state = UNAVAILABLE_DEPRECATED_SUPPRESS; + + /* We are processing a parameter-declaration-list and need to do this + early so that we have a decl for the pushdecl below. */ + if (declp && !cp_parser_error_occurred (parser)) + *declp = grokdeclarator (declarator, + &decl_specifiers, + PARM, + has_defarg_p, + &decl_specifiers.attributes); + + deprecated_state = DEPRECATED_NORMAL; + /* The restriction on defining new types applies only to the type of the parameter, not to the default argument. */ parser->type_definition_forbidden_message = saved_message; - cp_token *eq_token = NULL; + cp_token *eq_token = nullptr; /* If the next token is `=', then process a default argument. */ - if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) + if (has_defarg_p) { + /* And if we have a default argument, we must be dealing with a function + declaration, so we can push now to correctly handle + void fn (int i = sizeof (i)); + which is valid. */ + if (declp && *declp != error_mark_node && DECL_NAME (*declp)) + *declp = pushdecl (*declp); + tree type = decl_specifiers.type; token = cp_lexer_peek_token (parser->lexer); /* Used for diagnostics with an xobj parameter. */ @@ -28812,6 +28827,9 @@ cp_parser_parameter_declaration (cp_parser *parser, decl_spec_token_start->location, input_location); + if (declp && *declp != error_mark_node && param_loc != UNKNOWN_LOCATION) + DECL_SOURCE_LOCATION (*declp) = param_loc; + return make_parameter_declarator (&decl_specifiers, declarator, default_argument, diff --git a/gcc/testsuite/g++.dg/parse/defarg22.C b/gcc/testsuite/g++.dg/parse/defarg22.C new file mode 100644 index 00000000000..f393b95a591 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/defarg22.C @@ -0,0 +1,16 @@ +// PR c++/50479 +// { dg-do compile } + +void fn1 (int i = sizeof (i)) {} +void fn2 (int i, int = sizeof (i)) {} + +void +g () +{ + void fn4 (int i = sizeof (i)); + void fn5 (int i, int = sizeof (i)); + fn1 (); + fn2 (42); + fn4 (); + fn5 (42); +} diff --git a/gcc/testsuite/g++.dg/parse/defarg23.C b/gcc/testsuite/g++.dg/parse/defarg23.C new file mode 100644 index 00000000000..0aeb1b33394 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/defarg23.C @@ -0,0 +1,36 @@ +// PR c++/50479 +// { dg-do compile { target c++14 } } + +inline void +fn1 () +{ + int f (int i = []{ return 1; }()); +} + +auto l = [](int i = sizeof (i)) { }; +void fn2 (int i = []{ return 1; }()) {} +void fn3 (int x = []{ decltype(x) y{}; return y; }()) {} +void fn6 (int i = sizeof (i), decltype (i) = sizeof (i)) {} + +struct S { + int mfn1 (int i = sizeof (i)) { return i; } + int mfn2 (int i, int = sizeof (i)) { return i; } + int lm = [](int i6 = sizeof (i6)) { return i6; }(); + int mfn3 (int i = []{ return 1; }()) { return i; } + int i = mfn1 () + mfn2 (42) + mfn3 () + lm; +}; + +void +g () +{ + auto ll = [](int i5 = sizeof (i5)) { }; + void fn4 (int i = []{ return 1; }()); + void fn5 (int x = []{ decltype(x) y{}; return y; }()); + fn1 (); + fn2 (); + fn3 (); + fn4 (); + fn5 (); + fn6 (); + l (); +} diff --git a/gcc/testsuite/g++.dg/parse/defarg24.C b/gcc/testsuite/g++.dg/parse/defarg24.C new file mode 100644 index 00000000000..48b6f1f2a1c --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/defarg24.C @@ -0,0 +1,12 @@ +// PR c++/50479 +// { dg-do compile { target c++11 } } + +void fn1 (int x = []{ return x; }()) {} // { dg-error "use of parameter outside function body" } +void fn2 (int x, int = []{ return x; }()) {} // { dg-error "use of parameter outside function body" } + +void +g () +{ + void fn3 (int z = []{ return z; }()); // { dg-error "use of parameter outside function body" } + void fn4 (int z, int = []{ return z; }()); // { dg-error "use of parameter outside function body" } +} diff --git a/gcc/testsuite/g++.dg/parse/defarg25.C b/gcc/testsuite/g++.dg/parse/defarg25.C new file mode 100644 index 00000000000..95989885dfa --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/defarg25.C @@ -0,0 +1,22 @@ +// PR c++/62244 +// { dg-do compile { target c++11 } } + +int a; +void f (int a = a); // { dg-error "parameter .a. may not appear in this context" } + +int foo(int x = (decltype(x)(42))) { return 0; } + +static int value; + +int +bar (int &value = value) // { dg-error "parameter .value. may not appear in this context" } +{ + return value; +} + +void +g () +{ + bar (); +} + diff --git a/gcc/testsuite/g++.dg/parse/defarg26.C b/gcc/testsuite/g++.dg/parse/defarg26.C new file mode 100644 index 00000000000..170f7f5c0ec --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/defarg26.C @@ -0,0 +1,18 @@ +// PR c++/50479 +// { dg-do compile { target c++11 } } + +template +struct A; + +template<> +struct A { + static const int value = 1; +}; + +long foo; + +void bar(char foo = A::value) {} // { dg-error "incomplete type" } + +int main() { + bar(); +} diff --git a/gcc/testsuite/g++.dg/reflect/parm1.C b/gcc/testsuite/g++.dg/reflect/parm1.C index 8416be5b0db..c844c1dff3f 100644 --- a/gcc/testsuite/g++.dg/reflect/parm1.C +++ b/gcc/testsuite/g++.dg/reflect/parm1.C @@ -4,9 +4,8 @@ #include -// FIXME PR62244 -//consteval int fn(decltype(^^::) x = ^^x) { return 0; } -//constexpr int x = fn (); +consteval int fn(decltype(^^::) x = ^^x) { return 0; } +constexpr int x = fn (); consteval auto ref (std::meta::info r)