On 11/24/23 03:34, Jakub Jelinek wrote:
On Mon, Sep 18, 2023 at 07:12:40PM +0200, Jakub Jelinek via Gcc-patches wrote:
On Tue, Aug 22, 2023 at 09:39:11AM +0200, Jakub Jelinek via Gcc-patches wrote:
The following patch implements the C++26 P2169R4 paper.
As written in the PR, the patch expects that:
1) https://eel.is/c++draft/expr.prim.lambda.capture#2
    "Ignoring appearances in initializers of init-captures, an identifier
     or this shall not appear more than once in a lambda-capture."
    is adjusted such that name-independent lambda captures with initializers
    can violate this rule (but lambda captures which aren't name-independent
    can't appear after name-independent ones)
2) https://eel.is/c++draft/class.mem#general-5
    "A member shall not be declared twice in the member-specification,
     except that"
    having an exception that name-independent non-static data member
    declarations can appear multiple times (but again, if there is
    a member which isn't name-independent, it can't appear after
    name-independent ones)
3) it assumes that any name-independent declarations which weren't
    previously valid result in the _ lookups being ambiguous, not just
    if there are 2 _ declarations in the same scope, in particular the
    https://eel.is/c++draft/basic.scope#block-2 mentioned cases
4) it assumes that _ in static function/block scope structured bindings
    is never name-independent like in namespace scope structured bindings;
    it matches clang behavior and is consistent with e.g. static type _;
    not being name-independent both at namespace scope and at function/block
    scope

As you preferred in the PR, for local scope bindings, the ambiguous cases
use a TREE_LIST with the ambiguous cases which can often be directly fed
into print_candidates.  For member_vec after sorting/deduping, I chose to use
instead OVERLOAD with a new flag but only internally inside of the
member_vec, get_class_binding_direct turns it into a TREE_LIST.  There are
2 reasons for that, in order to keep the member_vec binary search fast, I
think it is better to keep OVL_NAME usable on all elements because having
to special case TREE_LIST would slow everything down, and the callers need
to be able to chain the results anyway and so need an unshared TREE_LIST
they can tweak/destroy anyway.
name-independent declarations (even in older standards) will not have
-Wunused{,-variable,-but-set-variable} or -Wshadow* warnings diagnosed, but
unlike e.g. the clang implementation, this patch does diagnose
-Wunused-parameter for parameters with _ names because they aren't
name-independent and one can just omit their name instead.

The patch no longer applies, because of the check_local_shadow changes.

The patch doesn't apply any longer again, because of the P2741R3
static_assert changes, both because I've included the first 2 hunks from
this patch in that commit and because there is a conflict in c_cpp_builtins.

Here is a new version which applies.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2023-11-23  Jakub Jelinek  <ja...@redhat.com>

        PR c++/110349
gcc/c-family/
        * c-cppbuiltin.cc (c_cpp_builtins): Predefine
        __cpp_placeholder_variables=202306L for C++26.
gcc/cp/
        * cp-tree.h: Implement C++26 P2169R4 - Placeholder variables with no
        name.
        (OVL_PLACEHOLDER_P): Define.

Throughout this patch let's follow the standard and avoid using the word "placeholder" to refer to name-independent decls. There are a lot of other things that could be meant by "placeholder", notably "auto".

        (add_capture): Add unsigned * argument.
        (name_independent_decl_p): New inline function.
        * name-lookup.cc (class name_lookup): Make ambiguous and
        add_value members public.
        (placeholder_linear_search): New function.
        (get_class_binding_direct): Handle member_vec_binary_search
        returning OVL_PLACEHOLDER_P OVERLOAD.  Use
        placeholder_linear_search rather than fields_linear_search
        for linear lookup of _ name if !want_type.
        (member_name_cmp): Sort name-independent declarations first.
        (member_vec_dedup): Handle name-independent declarations.
        (pop_local_binding): Handle binding->value being a TREE_LIST for
        ambiguous name-independent declarations.
        (supplement_binding): Handle name-independent declarations.
        (update_binding): Likewise.
        (check_local_shadow): Return tree rather than void, normally
        NULL_TREE but old for name-independent declarations which used
        to conflict with outer scope declaration.  Don't emit -Wshadow*
        warnings for name-independent declarations.
        (pushdecl): Handle name-independent declarations.
        (lookup_name): Likewise.
        * search.cc (lookup_field_r): Handle nval being a TREE_LIST.
        * lambda.cc (build_capture_proxy): Adjust for ___.<number>
        names of members.
        (add_capture): Add PLACEHOLDER_CNT argument.  Use ___.<number>
        name rather than ___ for second and following capture with
        _ name.
        (add_default_capture): Adjust add_capture caller.
        * decl.cc (poplevel): Don't warn about name-independent declarations.
        (reshape_init_class): If field is a TREE_LIST, emit an ambiguity
        error with list of candidates rather than error about non-existing
        non-static data member.
        * parser.cc (cp_parser_lambda_introducer): Adjust add_capture callers.
        Allow name-independent capture redeclarations.
        (cp_parser_decomposition_declaration): Set decl_specs.storage_class
        to sc_static for static structured bindings.
        * pt.cc (tsubst_lambda_expr): Adjust add_capture caller.
gcc/testsuite/
        * g++.dg/cpp26/placeholder1.C: New test.
        * g++.dg/cpp26/placeholder2.C: New test.
        * g++.dg/cpp26/placeholder3.C: New test.
        * g++.dg/cpp26/placeholder4.C: New test.
        * g++.dg/cpp26/placeholder5.C: New test.
        * g++.dg/cpp26/placeholder6.C: New test.
        * g++.dg/cpp26/feat-cxx26.C: Add __cpp_placeholder_variables test.


@@ -1843,6 +1909,34 @@ get_class_binding_direct (tree klass, tr
        val = member_vec_binary_search (member_vec, lookup);
        if (!val)
        ;
+      else if (TREE_CODE (val) == OVERLOAD && OVL_PLACEHOLDER_P (val))
+       {
+         if (want_type)
+           {
+             while (TREE_CODE (val) == OVERLOAD && OVL_PLACEHOLDER_P (val))
+               val = OVL_CHAIN (val);
+             if (STAT_HACK_P (val))
+               val = STAT_TYPE (val);
+             else if (!DECL_DECLARES_TYPE_P (val))
+               val = NULL_TREE;
+           }
+         else
+           {

Please add a comment here with your rationale from the commit message.

@@ -2479,10 +2609,27 @@ pop_local_binding (tree id, tree decl)
       away.  */
    if (binding->value == decl)
      binding->value = NULL_TREE;
+  else if (binding->type == decl)
+    binding->type = NULL_TREE;
    else
      {
-      gcc_checking_assert (binding->type == decl);
-      binding->type = NULL_TREE;
+      /* Name-independent variable was found after at least one declaration
+        with the same name.  */
+      gcc_assert (TREE_CODE (binding->value) == TREE_LIST);
+      if (TREE_VALUE (binding->value) != decl)
+       {
+         binding->value = nreverse (binding->value);
+         /* Skip over TREE_LISTs added for check_local_shadow detected

This should mention pushdecl, since that's where they're actually added (which, as mentioned below, I don't understand why).

+            declarations, formerly at the tail, now at the start of the
+            list.  */
+         while (TREE_PURPOSE (binding->value) == error_mark_node)
+           binding->value = TREE_CHAIN (binding->value);
+       }
+      gcc_assert (TREE_VALUE (binding->value) == decl);
+      binding->value = TREE_CHAIN (binding->value);
+      while (binding->value
+            && TREE_PURPOSE (binding->value) == error_mark_node)
+       binding->value = TREE_CHAIN (binding->value);
      }
if (!binding->value && !binding->type)

@@ -3780,7 +3988,20 @@ pushdecl (tree decl, bool hiding)
if (level->kind != sk_namespace)
        {
-         check_local_shadow (decl);
+         tree local_shadow = check_local_shadow (decl);
+         if (placeholder_p && local_shadow)
+           {
+             if (cxx_dialect < cxx26 && !placeholder_diagnosed_p)
+               pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wc__26_extensions,
+                        "placeholder variables only available with "
+                        "%<-std=c++2c%> or %<-std=gnu++2c%>");
+             placeholder_diagnosed_p = true;
+             if (old == NULL_TREE)
+               {
+                 old = build_tree_list (error_mark_node, local_shadow);
+                 TREE_TYPE (old) = error_mark_node;
+               }
+           }

This needs a rationale comment; I don't understand what the purpose is.

if (TREE_CODE (decl) == NAMESPACE_DECL)
            /* A local namespace alias.  */
@@ -7579,7 +7800,30 @@ lookup_name (tree name, LOOK_where where
                && (bool (want & LOOK_want::HIDDEN_LAMBDA)
                    || !is_lambda_ignored_entity (iter->value))
                && qualify_lookup (iter->value, want))
-             binding = iter->value;
+             {
+               binding = iter->value;
+               if (binding
+                   && TREE_CODE (binding) == TREE_LIST
+                   && name_independent_decl_p (TREE_VALUE (binding)))
+                 {
+                   for (tree b = binding; b; b = TREE_CHAIN (b))
+                     if (TREE_CHAIN (b) == NULL
+                         && TREE_CODE (TREE_VALUE (b)) == OVERLOAD)
+                       {
+                         /* If the scope has an overload with _ function
+                            declarations followed by at least one
+                            name-independent declaration, we shouldn't return
+                            iter->value but a new TREE_LIST containing the
+                            name-independent declaration(s) and functions
+                            from the OVERLOAD.  */

Why is the iter->value TREE_LIST unsuitable?

--- gcc/testsuite/g++.dg/cpp26/placeholder1.C.jj        2023-09-18 
12:42:16.309387948 +0200
+++ gcc/testsuite/g++.dg/cpp26/placeholder1.C   2023-09-18 12:59:09.286891703 
+0200
@@ -0,0 +1,194 @@
+// P2169R4 - A nice placeholder with no name
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wunused-variable -Wunused-but-set-variable -Wunused-parameter 
-Wshadow" }
+
+int a[3];
+
+void
+foo ()
+{
+  {
+    int _ = 1;
+    ++_;
+  }
+  {
+    int _ = 3;
+    ++_;
+    int _ = 4;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+  }
+  {
+    int _ = 5;
+    --_;
+    int _ = 6;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    int _ = 7;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+  }
+  {
+    auto [i, j, _] = a;                // { dg-warning "structured bindings only available 
with" "" { target c++14_down } }
+    ++i;
+    ++_;
+  }
+  {
+    auto [_, _, k] = a;                // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    ++k;                       // { dg-warning "structured bindings only available with" 
"" { target c++14_down } .-1 }
+  }
+  {
+    auto [i, j, _] = a;                // { dg-warning "structured bindings only available 
with" "" { target c++14_down } }
+    auto [_, k, l] = a;                // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    ++i;                       // { dg-warning "structured bindings only available with" 
"" { target c++14_down } .-1 }
+    ++l;
+  }
+  {
+    int _;
+    _ = 1;
+  }
+  {
+    int _ = 1;
+  }
+  {
+    int _;
+  }
+  {
+    static int _;              // { dg-warning "unused variable" }
+    int _ = 1;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+  }
+  {
+    extern int _ (int);
+    extern long _ (long);
+    extern float _ (float);
+    int _ = 1;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+  }
+  {
+    extern double _ (double);
+    extern short _ (short);
+    int _ = 1;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    int _ = 2;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+  }
+  {
+    int _ = 1;
+    {
+      int _ = 2;
+      ++_;
+    }
+    {
+      static int _ = 3;                // { dg-warning "declaration of '_' shadows 
a previous local" }

Perhaps we don't want to warn if either of the declarations is name-independent?

--- gcc/testsuite/g++.dg/cpp26/placeholder2.C.jj        2023-09-18 
12:42:16.309387948 +0200
+++ gcc/testsuite/g++.dg/cpp26/placeholder2.C   2023-09-18 12:42:16.309387948 
+0200
@@ -0,0 +1,171 @@
+// P2169R4 - A nice placeholder with no name
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+int a[3];
+
+void
+foo ()
+{
+  {
+    extern int _ (int);
+    int _ = 2;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    extern long _ (long);      // { dg-error "redeclared as different kind of 
entity" }
+  }
+  {
+    int _ = 3;
+    extern int _ (int);                // { dg-error "redeclared as different kind 
of entity" }
+  }
+  {
+    int _ = 4;
+    static int _ = 5;          // { dg-error "redeclaration of 'int _'" }
+  }
+  {
+    int _ = 6;
+    int _ = 7;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    ++_;                       // { dg-error "reference to '_' is ambiguous" }
+  }
+  {
+    int _ = 8;
+    int _ = 9;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    int _ = 10;                        // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    ++_;                       // { dg-error "reference to '_' is ambiguous" }
+  }
+  {
+    static int _ = 11;
+    static int _ = 12;         // { dg-error "redeclaration of 'int _'" }
+    int _ = 13;                        // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+  }
+  {
+    extern int _ (int);
+    extern long _ (long);
+    extern float _ (float);
+    int _ = 1;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    ++_;                       // { dg-error "reference to '_' is ambiguous" }
+  }
+  {
+    extern double _ (double);
+    extern short _ (short);
+    int _ = 1;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    ++_;                       // { dg-error "reference to '_' is ambiguous" }
+    int _ = 2;                 // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+    ++_;                       // { dg-error "reference to '_' is ambiguous" }
+  }
+  {
+    auto [i, _, _] = a;                // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+                               // { dg-warning "structured bindings only available with" 
"" { target c++14_down } .-1 }
+    ++_;                       // { dg-error "reference to '_' is ambiguous" }
+  }
+  {
+    auto [i, j, _] = a;                // { dg-warning "structured bindings only available 
with" "" { target c++14_down } }
+    auto [k, _, l] = a;                // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+                               // { dg-warning "structured bindings only available with" 
"" { target c++14_down } .-1 }
+    ++_;                       // { dg-error "reference to '_' is ambiguous" }
+  }
+  {
+    static auto [i, _, _] = a; // { dg-error "redeclaration of 'auto _'" }

Would it be easy to add a note explaining that static static bindings aren't name-independent?

+                               // { dg-warning "structured bindings only available with" 
"" { target c++14_down } .-1 }
+                               // { dg-warning "structured binding declaration can be 
'static' only in" "" { target c++17_down } .-2 }
+  }
+}
+
+int
+bar (int _ = 0)
+{
+  int _ = 1;                   // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+  ++_;                         // { dg-error "reference to '_' is ambiguous" }
+  return 0;
+}
+
+void
+baz ()
+{
+  if (int _ = bar ())
+    {
+      int _ = 6;               // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+      ++_;                     // { dg-error "reference to '_' is ambiguous" }
+    }
+  else
+    {
+      int _ = 7;               // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+      ++_;                     // { dg-error "reference to '_' is ambiguous" }
+    }
+  while (int _ = bar ())
+    {
+      int _ = 8;               // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+      ++_;                     // { dg-error "reference to '_' is ambiguous" }
+    }
+  for (int _ = bar (); _; ++_)
+    {
+      int _ = 9;               // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+      ++_;                     // { dg-error "reference to '_' is ambiguous" }
+    }
+}
+
+namespace A
+{
+  int _ = 1;
+  int _ = 1;                   // { dg-error "redefinition of 'int A::_'" }

Similarly, a note explaining that namespace-scope variables aren't name-independent.

+}
+
+namespace B
+{
+  auto [_, _, _] = a;          // { dg-error "redefinition of 'auto B::_'" }
+                               // { dg-warning "structured bindings only available with" 
"" { target c++14_down } .-1 }
+}
+
+void
+qux ()
+{
+  auto c = [_ = 2, _ = 3] () { // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+                               // { dg-warning "lambda capture initializers only available 
with" "" { target c++11_down } .-1 }
+    (void) _;                  // { dg-error "reference to '_' is ambiguous" }
+  };
+  {
+    int _ = 4;
+    auto d = [_, _ = 5] () {   // { dg-warning "placeholder variables only available 
with" "" { target c++23_down } }
+                               // { dg-warning "lambda capture initializers only available 
with" "" { target c++11_down } .-1 }
+      (void) _;                        // { dg-error "reference to '_' is 
ambiguous" }
+    };
+  }
+  auto e = [_ = 1] (int _) {}; // { dg-warning "lambda capture initializers only available 
with" "" { target c++11_down } }
+}                              // { dg-error "lambda parameter '_' previously declared as a 
capture" "" { target *-*-* } .-1 }
+
+void
+corge (int _, int _)           // { dg-error "redefinition of 'int _'" }

...likewise parameters.

Reply via email to