cor3ntin updated this revision to Diff 534185.
cor3ntin added a comment.

- Address Shafik's an Erich's comments
- Add missing serialization code


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153536/new/

https://reviews.llvm.org/D153536

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/Decl.h
  clang/include/clang/AST/DeclBase.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/IdentifierTable.h
  clang/include/clang/Sema/Lookup.h
  clang/include/clang/Sema/Sema.h
  clang/lib/Frontend/InitPreprocessor.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaLambda.cpp
  clang/lib/Sema/SemaLookup.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/lib/Serialization/ASTReaderDecl.cpp
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/Lexer/cxx-features.cpp
  clang/test/Lexer/unicode.c
  clang/test/SemaCXX/cxx2c-placeholder-vars.cpp
  clang/www/cxx_status.html

Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -145,7 +145,7 @@
  <tr>
   <td>Placeholder variables with no name</td>
   <td><a href="https://wg21.link/P2169R4";>P2169R4</a></td>
-  <td class="none" align="center">No</td>
+  <td class="unreleased" align="center">Clang 17</td>
  </tr>
 </table>
 </details>
Index: clang/test/SemaCXX/cxx2c-placeholder-vars.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/cxx2c-placeholder-vars.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang -cc1 -fsyntax-only -verify -std=c++2c -Wunused-parameter -Wunused %s
+
+void static_var() {
+    static int _; // expected-note {{previous definition is here}} \
+                  // expected-note {{candidate}}
+    static int _; // expected-error {{redefinition of '_'}}
+    int _;        // expected-warning {{placeholder variables are a C++2c extension}} \
+                  // expected-note {{candidate}}
+    _++; // expected-error{{reference to '_' is ambiguous}}
+}
+
+void static_var_2() {
+    int _; // expected-note {{previous definition is here}}
+    static int _; // expected-error {{redefinition of '_'}}
+}
+
+void bindings() {
+    int arr[4] = {0, 1, 2, 3};
+    auto [_, _, _, _] = arr; // expected-warning 3{{placeholder variables are a C++2c extension}} \
+                             // expected-note 4{{placeholder declared here}}
+    _ == 42; // expected-error {{referring to placeholder '_' is not allowed}}
+    {
+        auto [_, a, b, c] = arr;
+        auto [_, _, _, _] = arr; // expected-warning 4{{placeholder variables are a C++2c extension}}
+    }
+}
+
+void lambda() {
+    (void)[_ = 0, _ = 1] { // expected-warning {{placeholder variables are a C++2c extension}} \
+                           // expected-note 4{{placeholder declared here}} \\
+                           // expected-warning 2{{placeholder variable has no side effect}}
+        (void)_++; // expected-error {{referring to placeholder '_' is not allowed}}
+    };
+}
+
+namespace global_var {
+    int _; // expected-note {{previous definition is here}}
+    int _; // expected-error {{redefinition of '_'}}
+}
+
+namespace global_fun {
+void _();
+void _();
+
+void _() {} // expected-note {{previous definition is here}}
+void _() {} // expected-error {{redefinition of '_'}}
+void _(int){};
+}
+
+void extern_test() {
+    extern int _;
+    extern int _; // expected-note {{candidate}}
+    int _; //expected-note {{candidate}}
+    _++; // expected-error {{reference to '_' is ambiguous}}
+}
+
+
+struct Members {
+    int _; // expected-note {{placeholder declared here}}
+    int _; // expected-warning{{placeholder variables are a C++2c extension}} \
+           // expected-note {{placeholder declared here}}
+    void f() {
+        _++; // expected-error {{referring to placeholder '_' is not allowed}}
+    }
+};
+
+namespace using_ {
+int _; // expected-note {{target of using declaration}}
+void f() {
+    int _; // expected-note {{conflicting declaration}}
+    _ = 0;
+    using using_::_; // expected-error {{target of using declaration conflicts with declaration already in scope}}
+}
+}
+
+
+void call(int);
+void test_param(int _) {}
+void test_params(int _, int _); // expected-error {{redefinition of parameter '_'}} \
+                                // expected-note {{previous declaration is here}}
+
+template <auto _, auto _> // expected-error {{declaration of '_' shadows template parameter}} \
+                          // expected-note  {{template parameter is declared here}}
+auto i = 0;
+
+template <typename T>
+concept C = requires(T _, T _) {  // expected-error {{redefinition of parameter '_'}} \
+                                // expected-note {{previous declaration is here}}
+    T{};
+};
+
+struct S {
+    int a;
+};
+
+void f(S a, S _) { // expected-warning {{unused parameter 'a'}}
+
+}
Index: clang/test/Lexer/unicode.c
===================================================================
--- clang/test/Lexer/unicode.c
+++ clang/test/Lexer/unicode.c
@@ -27,7 +27,7 @@
 CHECK : The preprocessor should not complain about Unicode characters like ©.
 #endif
 
-        int _;
+int a;
 
 extern int X\UAAAAAAAA; // expected-error {{not allowed in an identifier}}
 int Y = '\UAAAAAAAA'; // expected-error {{invalid universal character}}
@@ -41,8 +41,8 @@
 extern int  \u1B4C;     // BALINESE LETTER ARCHAIC JNYA - Added in Unicode 14
 extern int  \U00016AA2; // TANGSA LETTER GA - Added in Unicode 14
 extern int  \U0001E4D0; // 𞓐 NAG MUNDARI LETTER O - Added in Unicode 15
-extern int _\N{TANGSA LETTER GA};
-extern int _\N{TANGSALETTERGA}; // expected-error {{'TANGSALETTERGA' is not a valid Unicode character name}} \
+extern int a\N{TANGSA LETTER GA};
+extern int a\N{TANGSALETTERGA}; // expected-error {{'TANGSALETTERGA' is not a valid Unicode character name}} \
                                 // expected-error {{expected ';' after top level declarator}} \
                                 // expected-note {{characters names in Unicode escape sequences are sensitive to case and whitespace}}
 
Index: clang/test/Lexer/cxx-features.cpp
===================================================================
--- clang/test/Lexer/cxx-features.cpp
+++ clang/test/Lexer/cxx-features.cpp
@@ -54,6 +54,10 @@
 #error "wrong value for __cpp_named_character_escapes"
 #endif
 
+#if check(placeholder_variables, 202306, 202306, 202306, 202306, 202306, 202306)
+#error "wrong value for __cpp_named_character_escapes"
+#endif
+
 #if check(explicit_this_parameter, 0, 0, 0, 0, 0, 0)
 #error "wrong value for __cpp_explicit_this_parameter"
 #endif
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -980,6 +980,7 @@
 void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) {
   VisitDeclaratorDecl(D);
   Record.push_back(D->isMutable());
+  Record.push_back(D->isPlaceholderVar());
 
   Record.push_back((D->StorageKind << 1) | D->BitField);
   if (D->StorageKind == FieldDecl::ISK_CapturedVLAType)
@@ -990,21 +991,14 @@
   if (!D->getDeclName())
     Record.AddDeclRef(Context.getInstantiatedFromUnnamedFieldDecl(D));
 
-  if (D->getDeclContext() == D->getLexicalDeclContext() &&
-      !D->hasAttrs() &&
-      !D->isImplicit() &&
-      !D->isUsed(false) &&
-      !D->isInvalidDecl() &&
-      !D->isReferenced() &&
-      !D->isTopLevelDeclInObjCContainer() &&
-      !D->isModulePrivate() &&
-      !D->getBitWidth() &&
-      !D->hasInClassInitializer() &&
-      !D->hasCapturedVLAType() &&
-      !D->hasExtInfo() &&
+  if (D->getDeclContext() == D->getLexicalDeclContext() && !D->hasAttrs() &&
+      !D->isImplicit() && !D->isUsed(false) && !D->isInvalidDecl() &&
+      !D->isReferenced() && !D->isPlaceholderVar() &&
+      !D->isTopLevelDeclInObjCContainer() && !D->isModulePrivate() &&
+      !D->getBitWidth() && !D->hasInClassInitializer() &&
+      !D->hasCapturedVLAType() && !D->hasExtInfo() &&
       !ObjCIvarDecl::classofKind(D->getKind()) &&
-      !ObjCAtDefsFieldDecl::classofKind(D->getKind()) &&
-      D->getDeclName())
+      !ObjCAtDefsFieldDecl::classofKind(D->getKind()) && D->getDeclName())
     AbbrevToUse = Writer.getDeclFieldAbbrev();
 
   Code = serialization::DECL_FIELD;
@@ -1056,6 +1050,7 @@
   Record.push_back(D->getTSCSpec());
   Record.push_back(D->getInitStyle());
   Record.push_back(D->isARCPseudoStrong());
+  Record.push_back(D->isPlaceholderVar());
   bool HasDeducedType = false;
   if (!isa<ParmVarDecl>(D)) {
     Record.push_back(D->isThisDeclarationADemotedDefinition());
@@ -1118,28 +1113,17 @@
     Record.push_back(VarNotTemplate);
   }
 
-  if (D->getDeclContext() == D->getLexicalDeclContext() &&
-      !D->hasAttrs() &&
-      !D->isImplicit() &&
-      !D->isUsed(false) &&
-      !D->isInvalidDecl() &&
-      !D->isReferenced() &&
-      !D->isTopLevelDeclInObjCContainer() &&
-      D->getAccess() == AS_none &&
-      !D->isModulePrivate() &&
-      !needsAnonymousDeclarationNumber(D) &&
+  if (D->getDeclContext() == D->getLexicalDeclContext() && !D->hasAttrs() &&
+      !D->isImplicit() && !D->isUsed(false) && !D->isInvalidDecl() &&
+      !D->isReferenced() && !D->isPlaceholderVar() &&
+      !D->isTopLevelDeclInObjCContainer() && D->getAccess() == AS_none &&
+      !D->isModulePrivate() && !needsAnonymousDeclarationNumber(D) &&
       D->getDeclName().getNameKind() == DeclarationName::Identifier &&
-      !D->hasExtInfo() &&
-      D->getFirstDecl() == D->getMostRecentDecl() &&
-      D->getKind() == Decl::Var &&
-      !D->isInline() &&
-      !D->isConstexpr() &&
-      !D->isInitCapture() &&
-      !D->isPreviousDeclInSameBlockScope() &&
-      !D->isEscapingByref() &&
-      !HasDeducedType &&
-      D->getStorageDuration() != SD_Static &&
-      !D->getDescribedVarTemplate() &&
+      !D->hasExtInfo() && D->getFirstDecl() == D->getMostRecentDecl() &&
+      D->getKind() == Decl::Var && !D->isInline() && !D->isConstexpr() &&
+      !D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() &&
+      !D->isEscapingByref() && !HasDeducedType &&
+      D->getStorageDuration() != SD_Static && !D->getDescribedVarTemplate() &&
       !D->getMemberSpecializationInfo())
     AbbrevToUse = Writer.getDeclVarAbbrev();
 
@@ -1211,6 +1195,7 @@
 void ASTDeclWriter::VisitBindingDecl(BindingDecl *D) {
   VisitValueDecl(D);
   Record.AddStmt(D->getBinding());
+  Record.push_back(D->isPlaceholderVar());
   Code = serialization::DECL_BINDING;
 }
 
@@ -2055,7 +2040,8 @@
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // TSIType
   // FieldDecl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isMutable
-  Abv->Add(BitCodeAbbrevOp(0));                       // StorageKind
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsPlaceholder
+  Abv->Add(BitCodeAbbrevOp(0));                         // StorageKind
   // Type Source Info
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // TypeLoc
@@ -2088,6 +2074,7 @@
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // TSIType
   // FieldDecl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isMutable
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsPlaceholder
   Abv->Add(BitCodeAbbrevOp(0));                       // InitStyle
   // ObjC Ivar
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // getAccessControl
@@ -2243,6 +2230,7 @@
   Abv->Add(BitCodeAbbrevOp(0));                       // TSCSpec
   Abv->Add(BitCodeAbbrevOp(0));                       // InitStyle
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsPlaceholder
   Abv->Add(BitCodeAbbrevOp(0));                       // Linkage
   Abv->Add(BitCodeAbbrevOp(0));                       // ModulesCodegen
   Abv->Add(BitCodeAbbrevOp(0));                       // VarKind (local enum)
@@ -2320,6 +2308,7 @@
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // TSCSpec
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // InitStyle
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsPlaceholder
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsThisDeclarationADemotedDefinition
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1501,6 +1501,7 @@
 void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) {
   VisitDeclaratorDecl(FD);
   FD->Mutable = Record.readInt();
+  FD->setIsPlaceholderVar(Record.readInt());
 
   unsigned Bits = Record.readInt();
   FD->StorageKind = Bits >> 1;
@@ -1579,6 +1580,7 @@
   VD->VarDeclBits.TSCSpec = Record.readInt();
   VD->VarDeclBits.InitStyle = Record.readInt();
   VD->VarDeclBits.ARCPseudoStrong = Record.readInt();
+  VD->setIsPlaceholderVar(Record.readInt());
   bool HasDeducedType = false;
   if (!isa<ParmVarDecl>(VD)) {
     VD->NonParmVarDeclBits.IsThisDeclarationADemotedDefinition =
@@ -1711,6 +1713,7 @@
 void ASTDeclReader::VisitBindingDecl(BindingDecl *BD) {
   VisitValueDecl(BD);
   BD->Binding = Record.readExpr();
+  BD->setIsPlaceholderVar(Record.readBool());
 }
 
 void ASTDeclReader::VisitFileScopeAsmDecl(FileScopeAsmDecl *AD) {
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1098,6 +1098,7 @@
   auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(),
                                     D->getIdentifier());
   NewBD->setReferenced(D->isReferenced());
+  NewBD->setIsPlaceholderVar(D->isPlaceholderVar());
   SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewBD);
   return NewBD;
 }
@@ -1196,6 +1197,7 @@
   }
 
   Var->setImplicit(D->isImplicit());
+  Var->setIsPlaceholderVar(D->isPlaceholderVar());
 
   if (Var->isStaticLocal())
     SemaRef.CheckStaticLocalForDllExport(Var);
@@ -1290,6 +1292,7 @@
   }
 
   Field->setImplicit(D->isImplicit());
+  Field->setIsPlaceholderVar(D->isPlaceholderVar());
   Field->setAccess(D->getAccess());
   Owner->addDecl(Field);
 
Index: clang/lib/Sema/SemaLookup.cpp
===================================================================
--- clang/lib/Sema/SemaLookup.cpp
+++ clang/lib/Sema/SemaLookup.cpp
@@ -508,6 +508,7 @@
   llvm::SmallDenseMap<QualType, unsigned, 16> UniqueTypes;
 
   bool Ambiguous = false;
+  bool ReferenceToPlaceHolderVariable = false;
   bool HasTag = false, HasFunction = false;
   bool HasFunctionTemplate = false, HasUnresolved = false;
   const NamedDecl *HasNonFunction = nullptr;
@@ -546,7 +547,7 @@
 
     // For non-type declarations, check for a prior lookup result naming this
     // canonical declaration.
-    if (!ExistingI) {
+    if (!D->isPlaceholderVar() && !ExistingI) {
       auto UniqueResult = Unique.insert(std::make_pair(D, I));
       if (!UniqueResult.second) {
         // We've seen this entity before.
@@ -590,7 +591,10 @@
           Decls[I] = Decls[--N];
           continue;
         }
-
+        if (D->isPlaceholderVar() && getContextForScopeMatching(D) ==
+                                         getContextForScopeMatching(Decls[I])) {
+          ReferenceToPlaceHolderVariable = true;
+        }
         Ambiguous = true;
       }
       HasNonFunction = D;
@@ -630,7 +634,9 @@
   if (HasNonFunction && (HasFunction || HasUnresolved))
     Ambiguous = true;
 
-  if (Ambiguous)
+  if (Ambiguous && ReferenceToPlaceHolderVariable)
+    setAmbiguous(LookupResult::AmbiguousReferenceToPlaceholderVariable);
+  else if (Ambiguous)
     setAmbiguous(LookupResult::AmbiguousReference);
   else if (HasUnresolved)
     ResultKind = LookupResult::FoundUnresolvedValue;
@@ -2856,6 +2862,13 @@
     break;
   }
 
+  case LookupResult::AmbiguousReferenceToPlaceholderVariable: {
+    Diag(NameLoc, diag::err_using_placeholder_variable) << Name << LookupRange;
+    for (auto *D : Result)
+      Diag(D->getLocation(), diag::note_reference_placeholder) << D;
+    break;
+  }
+
   case LookupResult::AmbiguousReference: {
     Diag(NameLoc, diag::err_ambiguous_reference) << Name << LookupRange;
 
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -1148,6 +1148,10 @@
         continue;
     }
 
+    if (Var && Var->isInitCapture() && C->Id->isPlaceholder()) {
+      Var->setIsPlaceholderVar(true);
+    }
+
     // C++11 [expr.prim.lambda]p10:
     //   [...] each such lookup shall find a variable with automatic storage
     //   duration declared in the reaching scope of the local lambda expression.
@@ -1168,11 +1172,15 @@
             << C->Id << It->second->getBeginLoc()
             << FixItHint::CreateRemoval(
                    SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc));
-      } else
+        Var->setInvalidDecl();
+      } else if (Var && Var->isPlaceholderVar()) {
+        DiagPlaceholderVariableDefinition(C->Loc);
+      } else {
         // Previous capture captured something different (one or both was
         // an init-capture): no fixit.
         Diag(C->Loc, diag::err_capture_more_than_once) << C->Id;
-      continue;
+        continue;
+      }
     }
 
     // Ignore invalid decls; they'll just confuse the code later.
@@ -1870,7 +1878,13 @@
   if (From.isVLATypeCapture())
     return false;
 
-  auto diag = Diag(From.getLocation(), diag::warn_unused_lambda_capture);
+  bool IsPlaceholder =
+      From.isInitCapture() && From.getVariable()->isPlaceholderVar();
+
+  auto diag =
+      Diag(From.getLocation(),
+           IsPlaceholder ? diag::warn_placeholder_variable_has_no_side_effect
+                         : diag::warn_unused_lambda_capture);
   if (From.isThisCapture())
     diag << "'this'";
   else
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -722,6 +722,12 @@
   return Invalid;
 }
 
+void Sema::DiagPlaceholderVariableDefinition(SourceLocation Loc) {
+  Diag(Loc, getLangOpts().CPlusPlus26
+                ? diag::ext_placeholder_var_definition
+                : diag::warn_cxx23_placeholder_var_definition);
+}
+
 NamedDecl *
 Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
                                    MultiTemplateParamsArg TemplateParamLists) {
@@ -877,6 +883,7 @@
   for (auto &B : D.getDecompositionDeclarator().bindings()) {
     // Check for name conflicts.
     DeclarationNameInfo NameInfo(B.Name, B.NameLoc);
+    IdentifierInfo *VarName = B.Name;
     LookupResult Previous(*this, NameInfo, LookupOrdinaryName,
                           ForVisibleRedeclaration);
     LookupName(Previous, S,
@@ -890,7 +897,7 @@
       Previous.clear();
     }
 
-    auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name);
+    auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, VarName);
 
     // Find the shadowed declaration before filtering for scope.
     NamedDecl *ShadowedDecl = D.getCXXScopeSpec().isEmpty()
@@ -902,10 +909,25 @@
     FilterLookupForScope(Previous, DC, S, ConsiderLinkage,
                          /*AllowInlineNamespace*/false);
 
+    bool IsPlaceholder = DS.getStorageClassSpec() != DeclSpec::SCS_static &&
+                         DC->isFunctionOrMethod() && VarName->isPlaceholder();
+    BD->setIsPlaceholderVar(IsPlaceholder);
     if (!Previous.empty()) {
-      auto *Old = Previous.getRepresentativeDecl();
-      Diag(B.NameLoc, diag::err_redefinition) << B.Name;
-      Diag(Old->getLocation(), diag::note_previous_definition);
+      if (IsPlaceholder) {
+        const bool sameDC = (Previous.end() - 1)
+                                ->getDeclContext()
+                                ->getRedeclContext()
+                                ->Equals(DC->getRedeclContext());
+        if (sameDC &&
+            isDeclInScope(*(Previous.end() - 1), CurContext, S, false)) {
+          Previous.clear();
+          DiagPlaceholderVariableDefinition(B.NameLoc);
+        }
+      } else {
+        auto *Old = Previous.getRepresentativeDecl();
+        Diag(B.NameLoc, diag::err_redefinition) << B.Name;
+        Diag(Old->getLocation(), diag::note_previous_definition);
+      }
     } else if (ShadowedDecl && !D.isRedeclaration()) {
       CheckShadow(BD, ShadowedDecl, Previous);
     }
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -1992,6 +1992,9 @@
     return false;
   }
 
+  if (D->isPlaceholderVar())
+    return false;
+
   if (D->hasAttr<UnusedAttr>() || D->hasAttr<ObjCPreciseLifetimeAttr>() ||
       D->hasAttr<CleanupAttr>())
     return false;
@@ -2144,7 +2147,9 @@
   GenerateFixForUnusedDecl(D, Context, Hint);
 
   unsigned DiagID;
-  if (isa<VarDecl>(D) && cast<VarDecl>(D)->isExceptionVariable())
+  if (D->isPlaceholderVar()) {
+    DiagID = diag::warn_placeholder_variable_has_no_side_effect;
+  } else if (isa<VarDecl>(D) && cast<VarDecl>(D)->isExceptionVariable())
     DiagID = diag::warn_unused_exception_param;
   else if (isa<LabelDecl>(D))
     DiagID = diag::warn_unused_label;
@@ -2163,6 +2168,9 @@
       VD->hasAttr<CleanupAttr>())
     return;
 
+  if (VD->isPlaceholderVar())
+    return;
+
   const auto *Ty = VD->getType().getTypePtr()->getBaseElementTypeUnsafe();
 
   if (Ty->isReferenceType() || Ty->isDependentType())
@@ -7435,6 +7443,7 @@
   DeclarationName Name = GetNameForDeclarator(D).getName();
 
   IdentifierInfo *II = Name.getAsIdentifierInfo();
+  bool IsPlaceholderVariable = false;
 
   if (D.isDecompositionDeclarator()) {
     // Take the name of the first declarator as our name for diagnostic
@@ -7453,6 +7462,19 @@
   DeclSpec::SCS SCSpec = D.getDeclSpec().getStorageClassSpec();
   StorageClass SC = StorageClassSpecToVarDeclStorageClass(D.getDeclSpec());
 
+  if (LangOpts.CPlusPlus && (DC->isClosure() || DC->isFunctionOrMethod()) &&
+      SC != SC_Static && SC != SC_Extern && II && II->isPlaceholder()) {
+    IsPlaceholderVariable = true;
+    if (!Previous.empty()) {
+      auto Decl = *Previous.begin();
+      const bool sameDC = Decl->getDeclContext()->getRedeclContext()->Equals(
+          DC->getRedeclContext());
+      if (sameDC && isDeclInScope(Decl, CurContext, S, false)) {
+        DiagPlaceholderVariableDefinition(D.getIdentifierLoc());
+      }
+    }
+  }
+
   // dllimport globals without explicit storage class are treated as extern. We
   // have to change the storage class this early to get the right DeclContext.
   if (SC == SC_None && !DC->isRecord() &&
@@ -7723,6 +7745,7 @@
   // Set the lexical context. If the declarator has a C++ scope specifier, the
   // lexical context will be different from the semantic context.
   NewVD->setLexicalDeclContext(CurContext);
+  NewVD->setIsPlaceholderVar(IsPlaceholderVariable);
   if (NewTemplate)
     NewTemplate->setLexicalDeclContext(CurContext);
 
@@ -8021,7 +8044,7 @@
       NewVD->setInvalidDecl();
     }
 
-    if (!IsVariableTemplateSpecialization)
+    if (!IsVariableTemplateSpecialization && !IsPlaceholderVariable)
       D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous));
 
     // CheckVariableDeclaration will set NewVD as invalid if something is in
@@ -8059,7 +8082,7 @@
   }
 
   // Diagnose shadowed variables iff this isn't a redeclaration.
-  if (ShadowedDecl && !D.isRedeclaration())
+  if (!IsPlaceholderVariable && ShadowedDecl && !D.isRedeclaration())
     CheckShadow(NewVD, ShadowedDecl, Previous);
 
   ProcessPragmaWeak(S, NewVD);
@@ -8293,6 +8316,10 @@
     }
   }
 
+  // Never warn about shadowing placeholder variable
+  if (ShadowedDecl->isPlaceholderVar())
+    return;
+
   // Only warn about certain kinds of shadowing for class members.
   if (NewDC && NewDC->isRecord()) {
     // In particular, don't warn about shadowing non-class members.
@@ -14753,17 +14780,17 @@
     LookupResult R(*this, II, D.getIdentifierLoc(), LookupOrdinaryName,
                    ForVisibleRedeclaration);
     LookupName(R, S);
-    if (R.isSingleResult()) {
-      NamedDecl *PrevDecl = R.getFoundDecl();
-      if (PrevDecl->isTemplateParameter()) {
+    if (!R.empty()) {
+      NamedDecl *PrevDecl = *R.begin();
+      if (R.isSingleResult() && PrevDecl->isTemplateParameter()) {
         // Maybe we will complain about the shadowed template parameter.
         DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), PrevDecl);
         // Just pretend that we didn't see the previous declaration.
         PrevDecl = nullptr;
-      } else if (S->isDeclScope(PrevDecl)) {
+      }
+      if (PrevDecl && S->isDeclScope(PrevDecl)) {
         Diag(D.getIdentifierLoc(), diag::err_param_redefinition) << II;
         Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
-
         // Recover by removing the name
         II = nullptr;
         D.SetIdentifier(nullptr, D.getIdentifierLoc());
@@ -14832,7 +14859,8 @@
 
   for (const ParmVarDecl *Parameter : Parameters) {
     if (!Parameter->isReferenced() && Parameter->getDeclName() &&
-        !Parameter->hasAttr<UnusedAttr>()) {
+        !Parameter->hasAttr<UnusedAttr>() &&
+        !Parameter->getIdentifier()->isPlaceholder()) {
       Diag(Parameter->getLocation(), diag::warn_unused_parameter)
         << Parameter->getDeclName();
     }
@@ -18184,10 +18212,15 @@
   if (InvalidDecl)
     NewFD->setInvalidDecl();
 
+  NewFD->setIsPlaceholderVar(LangOpts.CPlusPlus && II && II->isPlaceholder());
   if (PrevDecl && !isa<TagDecl>(PrevDecl)) {
-    Diag(Loc, diag::err_duplicate_member) << II;
-    Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
-    NewFD->setInvalidDecl();
+    if (isa<FieldDecl>(PrevDecl) && PrevDecl->isPlaceholderVar()) {
+      DiagPlaceholderVariableDefinition(Loc);
+    } else {
+      Diag(Loc, diag::err_duplicate_member) << II;
+      Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
+      NewFD->setInvalidDecl();
+    }
   }
 
   if (!InvalidDecl && getLangOpts().CPlusPlus) {
Index: clang/lib/Frontend/InitPreprocessor.cpp
===================================================================
--- clang/lib/Frontend/InitPreprocessor.cpp
+++ clang/lib/Frontend/InitPreprocessor.cpp
@@ -708,6 +708,7 @@
   if (LangOpts.CPlusPlus11)
     Builder.defineMacro("__cpp_static_call_operator", "202207L");
   Builder.defineMacro("__cpp_named_character_escapes", "202207L");
+  Builder.defineMacro("__cpp_placeholder_variables", "202306L");
 
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2939,6 +2939,7 @@
   NamedDecl *
   ActOnDecompositionDeclarator(Scope *S, Declarator &D,
                                MultiTemplateParamsArg TemplateParamLists);
+  void DiagPlaceholderVariableDefinition(SourceLocation Loc);
   // Returns true if the variable declaration is a redeclaration
   bool CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous);
   void CheckVariableDeclarationType(VarDecl *NewVD);
Index: clang/include/clang/Sema/Lookup.h
===================================================================
--- clang/include/clang/Sema/Lookup.h
+++ clang/include/clang/Sema/Lookup.h
@@ -117,6 +117,17 @@
     /// @endcode
     AmbiguousReference,
 
+    /// Name lookup results in an ambiguity because multiple placeholder
+    /// variables were found in the same scope
+    /// @code
+    /// void f() {
+    ///    int _ = 0;
+    ///    int _ = 0;
+    ///    return _; // ambiguous use of placeholder variable
+    /// }
+    /// @endcode
+    AmbiguousReferenceToPlaceholderVariable,
+
     /// Name lookup results in an ambiguity because an entity with a
     /// tag name was hidden by an entity with an ordinary name from
     /// a different context.
Index: clang/include/clang/Basic/IdentifierTable.h
===================================================================
--- clang/include/clang/Basic/IdentifierTable.h
+++ clang/include/clang/Basic/IdentifierTable.h
@@ -462,6 +462,9 @@
   /// If the identifier is an "uglified" reserved name, return a cleaned form.
   /// e.g. _Foo => Foo. Otherwise, just returns the name.
   StringRef deuglifiedName() const;
+  bool isPlaceholder() const {
+    return getLength() == 1 && getNameStart()[0] == '_';
+  }
 
   /// Provide less than operator for lexicographical sorting.
   bool operator<(const IdentifierInfo &RHS) const {
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6585,6 +6585,19 @@
   InGroup<DiagGroup<"atomic-access">>, DefaultError;
 
 // Expressions.
+def err_using_placeholder_variable : Error<
+  "referring to placeholder '_' is not allowed">;
+def note_reference_placeholder : Note<
+  "placeholder declared here">;
+def warn_placeholder_variable_has_no_side_effect : Warning<
+  "placeholder variable has no side effect">, InGroup<UnusedValue>;
+
+def ext_placeholder_var_definition : ExtWarn<
+  "placeholder variables are a C++2c extension">, InGroup<CXX26>;
+def warn_cxx23_placeholder_var_definition : Warning<
+  "placeholder variables are incompatible with C++ standards before C++2c">,
+  DefaultIgnore, InGroup<CXXPre26Compat>;
+
 def ext_sizeof_alignof_function_type : Extension<
   "invalid application of '%0' to a function type">, InGroup<PointerArith>;
 def ext_sizeof_alignof_void_type : Extension<
Index: clang/include/clang/AST/DeclBase.h
===================================================================
--- clang/include/clang/AST/DeclBase.h
+++ clang/include/clang/AST/DeclBase.h
@@ -337,6 +337,10 @@
   /// Otherwise, it is the linkage + 1.
   mutable unsigned CacheValidAndLinkage : 3;
 
+  /// Whether this declaration denotes a placeholder that can be
+  /// redefined in the same scope.
+  unsigned IsPlaceholder : 1;
+
   /// Allocate memory for a deserialized declaration.
   ///
   /// This routine must be used to allocate memory for any declaration that is
@@ -385,7 +389,7 @@
         Implicit(false), Used(false), Referenced(false),
         TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0),
         IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
-        CacheValidAndLinkage(0) {
+        CacheValidAndLinkage(0), IsPlaceholder(false) {
     if (StatisticsEnabled) add(DK);
   }
 
@@ -394,7 +398,7 @@
         Used(false), Referenced(false), TopLevelDeclInObjCContainer(false),
         Access(AS_none), FromASTFile(0),
         IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
-        CacheValidAndLinkage(0) {
+        CacheValidAndLinkage(0), IsPlaceholder(false) {
     if (StatisticsEnabled) add(DK);
   }
 
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -452,6 +452,10 @@
     return hasCachedLinkage();
   }
 
+  bool isPlaceholderVar() const { return Decl::IsPlaceholder; }
+
+  void setIsPlaceholderVar(bool Set) { Decl::IsPlaceholder = Set; }
+
   /// Looks through UsingDecls and ObjCCompatibleAliasDecls for
   /// the underlying named decl.
   NamedDecl *getUnderlyingDecl() {
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -132,6 +132,8 @@
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 - Compiler flags ``-std=c++2c`` and ``-std=gnu++2c`` have been added for experimental C++2c implementation work.
+- Implemented `P2169R4: A nice placeholder with no name <https://wg21.link/P2169R4>`_. This allows to use `_`
+  as a variable name multiple times in the same scope and is supported in all C++ language modes as an extension.
 
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to