]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: deferred parsing of default arguments [PR50479]
authorMarek Polacek <polacek@redhat.com>
Mon, 11 May 2026 21:19:42 +0000 (17:19 -0400)
committerMarek Polacek <polacek@redhat.com>
Tue, 12 May 2026 14:58:43 +0000 (10:58 -0400)
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 <jason@redhat.com>
gcc/cp/parser.cc
gcc/testsuite/g++.dg/parse/defarg22.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/defarg23.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/defarg24.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/defarg25.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/defarg26.C [new file with mode: 0644]
gcc/testsuite/g++.dg/reflect/parm1.C

index d128de771b59e94c46e45faa27cb92f4fed7f36a..c08f4cee920d3d18a5f30c7e921e34f6fc0db7b8 100644 (file)
@@ -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<tree> *);
 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,
-                                &parameter->decl_specifiers,
-                                PARM,
-                                parameter->default_argument != NULL_TREE,
-                                &parameter->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 (file)
index 0000000..f393b95
--- /dev/null
@@ -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 (file)
index 0000000..0aeb1b3
--- /dev/null
@@ -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 (file)
index 0000000..48b6f1f
--- /dev/null
@@ -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 (file)
index 0000000..9598988
--- /dev/null
@@ -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 (file)
index 0000000..170f7f5
--- /dev/null
@@ -0,0 +1,18 @@
+// PR c++/50479
+// { dg-do compile { target c++11 } }
+
+template<class>
+struct A;
+
+template<>
+struct A<long> {
+  static const int value = 1;
+};
+
+long foo;
+
+void bar(char foo = A<decltype(foo)>::value) {} // { dg-error "incomplete type" }
+
+int main() {
+  bar();
+}
index 8416be5b0dbed764f9ed5893bf3901d27f73619b..c844c1dff3f8b4f51f6644af714a1217d72c8af0 100644 (file)
@@ -4,9 +4,8 @@
 
 #include <meta>
 
-// 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)