[clang] [clang] Implement CWG2877 "Type-only lookup for using-enum-declarator" (PR #95399)

Vlad Serebrennikov via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 20 05:38:49 PDT 2024


https://github.com/Endilll updated https://github.com/llvm/llvm-project/pull/95399

>From a7b6a8b667d48d8e70f3e5fcbaaa44a72ad885db Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 13 Jun 2024 14:35:08 +0300
Subject: [PATCH 1/5] [clang] Implement CWG2877 "Type-only lookup for
 using-enum-declarator"

---
 clang/include/clang/Sema/Sema.h         |  2 +-
 clang/lib/Parse/ParseDeclCXX.cpp        | 44 +++++++++++++++++++++----
 clang/lib/Sema/SemaDeclCXX.cpp          | 19 ++++++-----
 clang/test/CXX/drs/cwg26xx.cpp          |  5 ++-
 clang/test/CXX/drs/cwg28xx.cpp          | 10 ++----
 clang/test/SemaCXX/cxx20-using-enum.cpp | 12 ++++---
 clang/www/cxx_dr_status.html            | 10 ++++--
 7 files changed, 70 insertions(+), 32 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4d4579fcfd456..ccabd4925b8a3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4032,7 +4032,7 @@ class Sema final : public SemaBase {
   Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS,
                                   SourceLocation UsingLoc,
                                   SourceLocation EnumLoc,
-                                  SourceLocation IdentLoc, IdentifierInfo &II,
+                                  SourceRange TyLoc, const IdentifierInfo &II, ParsedType Ty,
                                   CXXScopeSpec *SS = nullptr);
   Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS,
                               MultiTemplateParamsArg TemplateParams,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 9a4a777f575b2..189fcc5440292 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -692,7 +692,7 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
 ///     using-enum-declaration: [C++20, dcl.enum]
 ///       'using' elaborated-enum-specifier ;
 ///       The terminal name of the elaborated-enum-specifier undergoes
-///       ordinary lookup
+///       type-only lookup
 ///
 ///     elaborated-enum-specifier:
 ///       'enum' nested-name-specifier[opt] identifier
@@ -738,16 +738,48 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
       return nullptr;
     }
 
-    if (!Tok.is(tok::identifier)) {
+    Decl *UED = nullptr;
+
+    if (Tok.is(tok::identifier)) {
+      IdentifierInfo *IdentInfo = Tok.getIdentifierInfo();
+      SourceLocation IdentLoc = ConsumeToken();
+
+      ParsedType Type = Actions.getTypeName(*IdentInfo, IdentLoc, getCurScope(), &SS, /*isClassName=*/true,
+                  /*HasTrailingDot=*/false,
+                  /*ObjectType=*/nullptr, /*IsCtorOrDtorName=*/false,
+                  /*WantNontrivialTypeSourceInfo=*/true);
+
+      UED = Actions.ActOnUsingEnumDeclaration(
+          getCurScope(), AS, UsingLoc, UELoc, IdentLoc, *IdentInfo, Type, &SS);
+    }
+    else if (Tok.is(tok::annot_template_id)) {
+      TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+
+      if (TemplateId->mightBeType()) {
+        AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No,
+                                      /*IsClassName=*/true);
+
+        assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
+        TypeResult Type = getTypeAnnotation(Tok);
+        SourceRange Loc = Tok.getAnnotationRange();
+        ConsumeAnnotationToken();
+
+        UED = Actions.ActOnUsingEnumDeclaration(
+        getCurScope(), AS, UsingLoc, UELoc, Loc, *TemplateId->Name, Type.get(), &SS);
+      }
+      else {
+        Diag(Tok.getLocation(), diag::err_using_enum_not_enum)
+            << TemplateId->Name->getName()
+            << SourceRange(TemplateId->TemplateNameLoc, TemplateId->RAngleLoc);
+      }
+    }
+    else {
       Diag(Tok.getLocation(), diag::err_using_enum_expect_identifier)
           << Tok.is(tok::kw_enum);
       SkipUntil(tok::semi);
       return nullptr;
     }
-    IdentifierInfo *IdentInfo = Tok.getIdentifierInfo();
-    SourceLocation IdentLoc = ConsumeToken();
-    Decl *UED = Actions.ActOnUsingEnumDeclaration(
-        getCurScope(), AS, UsingLoc, UELoc, IdentLoc, *IdentInfo, &SS);
+    
     if (!UED) {
       SkipUntil(tok::semi);
       return nullptr;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 37f0df2a6a27d..fb9e38d6b5bc9 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -12395,22 +12395,23 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, AccessSpecifier AS,
 Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
                                       SourceLocation UsingLoc,
                                       SourceLocation EnumLoc,
-                                      SourceLocation IdentLoc,
-                                      IdentifierInfo &II, CXXScopeSpec *SS) {
+                                      SourceRange TyLoc, const IdentifierInfo &II,
+                                      ParsedType Ty, CXXScopeSpec *SS) {
   assert(!SS->isInvalid() && "ScopeSpec is invalid");
   TypeSourceInfo *TSI = nullptr;
-  QualType EnumTy = GetTypeFromParser(
-      getTypeName(II, IdentLoc, S, SS, /*isClassName=*/false,
-                  /*HasTrailingDot=*/false,
-                  /*ObjectType=*/nullptr, /*IsCtorOrDtorName=*/false,
-                  /*WantNontrivialTypeSourceInfo=*/true),
-      &TSI);
+  SourceLocation IdentLoc = TyLoc.getBegin();
+  QualType EnumTy = GetTypeFromParser(Ty, &TSI);
   if (EnumTy.isNull()) {
     Diag(IdentLoc, SS && isDependentScopeSpecifier(*SS)
                        ? diag::err_using_enum_is_dependent
                        : diag::err_unknown_typename)
         << II.getName()
-        << SourceRange(SS ? SS->getBeginLoc() : IdentLoc, IdentLoc);
+        << SourceRange(SS ? SS->getBeginLoc() : IdentLoc, TyLoc.getEnd());
+    return nullptr;
+  }
+
+  if (EnumTy->isDependentType()) {
+    Diag(IdentLoc, diag::err_using_enum_is_dependent);
     return nullptr;
   }
 
diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp
index 2b17c8101438d..e180c93011a7e 100644
--- a/clang/test/CXX/drs/cwg26xx.cpp
+++ b/clang/test/CXX/drs/cwg26xx.cpp
@@ -7,7 +7,7 @@
 // RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown -pedantic-errors %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
 
 
-namespace cwg2621 { // cwg2621: 16
+namespace cwg2621 { // cwg2621: sup 2877
 #if __cplusplus >= 202002L
 enum class E { a };
 namespace One {
@@ -17,9 +17,8 @@ auto v = a;
 }
 namespace Two {
 using cwg2621::E;
-int E; // we see this
+int E; // ignored by type-only lookup
 using enum E;
-// since-cxx20-error at -1 {{unknown type name E}}
 }
 #endif
 }
diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index da81eccc8dc22..67aa34484fe8b 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -110,22 +110,18 @@ struct A {
 
 } // namespace cwg2858
 
-namespace cwg2877 { // cwg2877: no tentatively ready 2024-05-31
+namespace cwg2877 { // cwg2877: 19 tentatively ready 2024-05-31
 #if __cplusplus >= 202002L
 enum E { x };
 void f() {
   int E;
-  // FIXME: OK, names ::E
-  using enum E;
-  // since-cxx20-error at -1 {{unknown type name E}}
+  using enum E;   // OK, names ::E
 }
 using F = E;
 using enum F;     // OK, designates ::E
 template<class T> using EE = T;
 void g() {
-  // FIXME: OK, designates ::E
-  using enum EE<E>;
-  // since-cxx20-error at -1 {{using enum requires an enum or typedef name}}
+  using enum EE<E>;  // OK, designates ::E
 }
 #endif
 } // namespace cwg2877
diff --git a/clang/test/SemaCXX/cxx20-using-enum.cpp b/clang/test/SemaCXX/cxx20-using-enum.cpp
index d16df4e1b9d3c..14ef4b29925a1 100644
--- a/clang/test/SemaCXX/cxx20-using-enum.cpp
+++ b/clang/test/SemaCXX/cxx20-using-enum.cpp
@@ -148,13 +148,10 @@ template <int I> struct C {
   enum class D { d,
                  e,
                  f };
-  using enum D;
-
-  static constexpr int W = int(f) + I;
+  using enum D; // expected-error {{using-enum cannot name a dependent type}}
 };
 
 static_assert(C<2>::V == 4);
-static_assert(C<20>::W == 22);
 
 } // namespace Seven
 
@@ -241,6 +238,13 @@ TPLa<int> a;
 
 } // namespace Thirteen
 
+namespace Fourteen {
+template<typename T>
+int A = T();
+
+using enum A<int>; // expected-error {{A is not an enumerated type}}
+} // namespace Fourteen
+
 namespace GH58057 {
 struct Wrap {
 enum Things {
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 5e2ab06701703..31f5dbde05908 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -15534,7 +15534,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/2621.html">2621</a></td>
     <td>C++23</td>
     <td>Kind of lookup for <TT>using enum</TT> declarations</td>
-    <td class="full" align="center">Clang 16</td>
+    <td title="Clang 19 implements 2024-05-31 resolution" align="center">Superseded by <a href="#2877">2877</a></td>
   </tr>
   <tr id="2622">
     <td><a href="https://cplusplus.github.io/CWG/issues/2622.html">2622</a></td>
@@ -17071,7 +17071,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/2877.html">2877</a></td>
     <td>tentatively ready</td>
     <td>Type-only lookup for <I>using-enum-declarator</I></td>
-    <td title="Clang does not implement 2024-05-31 resolution" align="center">Not Resolved*</td>
+    <td title="Clang 19 implements 2024-05-31 resolution" align="center">Not Resolved*</td>
   </tr>
   <tr class="open" id="2878">
     <td><a href="https://cplusplus.github.io/CWG/issues/2878.html">2878</a></td>
@@ -17198,6 +17198,12 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td>open</td>
     <td>Clarify implicit conversion sequence from <I>cv</I> <TT>T</TT> to <TT>T</TT></td>
     <td align="center">Not resolved</td>
+  </tr>
+  <tr class="open" id="2899">
+    <td><a href="https://cplusplus.github.io/CWG/issues/2899.html">2899</a></td>
+    <td>open</td>
+    <td>Bad value representations should cause undefined behavior</td>
+    <td align="center">Not resolved</td>
   </tr></table>
 
 </div>

>From 40ad6801cd84cfc5a20abfaedc8e122f99891cac Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 13 Jun 2024 14:50:13 +0300
Subject: [PATCH 2/5] Run clang-format

---
 clang/include/clang/Sema/Sema.h  |  4 ++--
 clang/lib/Parse/ParseDeclCXX.cpp | 25 ++++++++++++-------------
 clang/lib/Sema/SemaDeclCXX.cpp   |  6 +++---
 3 files changed, 17 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ccabd4925b8a3..dc059f781dcb2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4031,8 +4031,8 @@ class Sema final : public SemaBase {
                               const ParsedAttributesView &AttrList);
   Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS,
                                   SourceLocation UsingLoc,
-                                  SourceLocation EnumLoc,
-                                  SourceRange TyLoc, const IdentifierInfo &II, ParsedType Ty,
+                                  SourceLocation EnumLoc, SourceRange TyLoc,
+                                  const IdentifierInfo &II, ParsedType Ty,
                                   CXXScopeSpec *SS = nullptr);
   Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS,
                               MultiTemplateParamsArg TemplateParams,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 189fcc5440292..51e065a6c6a86 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -744,15 +744,15 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
       IdentifierInfo *IdentInfo = Tok.getIdentifierInfo();
       SourceLocation IdentLoc = ConsumeToken();
 
-      ParsedType Type = Actions.getTypeName(*IdentInfo, IdentLoc, getCurScope(), &SS, /*isClassName=*/true,
-                  /*HasTrailingDot=*/false,
-                  /*ObjectType=*/nullptr, /*IsCtorOrDtorName=*/false,
-                  /*WantNontrivialTypeSourceInfo=*/true);
+      ParsedType Type = Actions.getTypeName(
+          *IdentInfo, IdentLoc, getCurScope(), &SS, /*isClassName=*/true,
+          /*HasTrailingDot=*/false,
+          /*ObjectType=*/nullptr, /*IsCtorOrDtorName=*/false,
+          /*WantNontrivialTypeSourceInfo=*/true);
 
       UED = Actions.ActOnUsingEnumDeclaration(
           getCurScope(), AS, UsingLoc, UELoc, IdentLoc, *IdentInfo, Type, &SS);
-    }
-    else if (Tok.is(tok::annot_template_id)) {
+    } else if (Tok.is(tok::annot_template_id)) {
       TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
 
       if (TemplateId->mightBeType()) {
@@ -764,22 +764,21 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
         SourceRange Loc = Tok.getAnnotationRange();
         ConsumeAnnotationToken();
 
-        UED = Actions.ActOnUsingEnumDeclaration(
-        getCurScope(), AS, UsingLoc, UELoc, Loc, *TemplateId->Name, Type.get(), &SS);
-      }
-      else {
+        UED = Actions.ActOnUsingEnumDeclaration(getCurScope(), AS, UsingLoc,
+                                                UELoc, Loc, *TemplateId->Name,
+                                                Type.get(), &SS);
+      } else {
         Diag(Tok.getLocation(), diag::err_using_enum_not_enum)
             << TemplateId->Name->getName()
             << SourceRange(TemplateId->TemplateNameLoc, TemplateId->RAngleLoc);
       }
-    }
-    else {
+    } else {
       Diag(Tok.getLocation(), diag::err_using_enum_expect_identifier)
           << Tok.is(tok::kw_enum);
       SkipUntil(tok::semi);
       return nullptr;
     }
-    
+
     if (!UED) {
       SkipUntil(tok::semi);
       return nullptr;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index fb9e38d6b5bc9..d65c7df585bac 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -12394,9 +12394,9 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, AccessSpecifier AS,
 
 Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
                                       SourceLocation UsingLoc,
-                                      SourceLocation EnumLoc,
-                                      SourceRange TyLoc, const IdentifierInfo &II,
-                                      ParsedType Ty, CXXScopeSpec *SS) {
+                                      SourceLocation EnumLoc, SourceRange TyLoc,
+                                      const IdentifierInfo &II, ParsedType Ty,
+                                      CXXScopeSpec *SS) {
   assert(!SS->isInvalid() && "ScopeSpec is invalid");
   TypeSourceInfo *TSI = nullptr;
   SourceLocation IdentLoc = TyLoc.getBegin();

>From d6868d3de4f70fe3cca7ba8409e8cbfeedae1b1a Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 13 Jun 2024 15:02:57 +0300
Subject: [PATCH 3/5] Add a release notes entry

---
 clang/docs/ReleaseNotes.rst | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8c2f737836a9d..3ac1029632ffe 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -257,6 +257,9 @@ Resolutions to C++ Defect Reports
 - P0522 implementation is enabled by default in all language versions, and
   provisional wording for CWG2398 is implemented.
 
+- Clang now performs type-only lookup for the name in ``using enum`` declaration.
+  (`CWG2877: Type-only lookup for using-enum-declarator <https://cplusplus.github.io/CWG/issues/2877.html>`_).
+
 - Clang now requires a template argument list after a template keyword.
   (`CWG96: Syntactic disambiguation using the template keyword <https://cplusplus.github.io/CWG/issues/96.html>`_).
 

>From c19bc288688c73571625aafbd7f0936c1f4ee378 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 20 Jun 2024 00:01:10 +0300
Subject: [PATCH 4/5] Add fixmes that similarities between `using enum` and
 base specifier code should be factored out

---
 clang/lib/Parse/ParseDeclCXX.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 51e065a6c6a86..08c0d5aaaff5e 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -740,6 +740,8 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
 
     Decl *UED = nullptr;
 
+    // FIXME: identifier and annot_template_id handling is very similar to
+    // ParseBaseTypeSpecifier. It should be factored out into a function.
     if (Tok.is(tok::identifier)) {
       IdentifierInfo *IdentInfo = Tok.getIdentifierInfo();
       SourceLocation IdentLoc = ConsumeToken();
@@ -1434,6 +1436,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
   }
 
   // Check whether we have a template-id that names a type.
+  // FIXME: identifier and annot_template_id handling in ParseUsingDeclaration
+  // work very similarly. It should be refactored into a separate function.
   if (Tok.is(tok::annot_template_id)) {
     TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
     if (TemplateId->mightBeType()) {

>From 2cc8a6640b43cd929f6b57cd5417788109039615 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 20 Jun 2024 15:38:31 +0300
Subject: [PATCH 5/5] Pass `/*IsTypename*/=true` to
 `ParseOptionalCXXScopeSpecifier` that handles `using enum`

---
 clang/lib/Parse/ParseDeclCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 08c0d5aaaff5e..a28baa1b659a8 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -724,7 +724,7 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
                                        /*ObectHasErrors=*/false,
                                        /*EnteringConttext=*/false,
                                        /*MayBePseudoDestructor=*/nullptr,
-                                       /*IsTypename=*/false,
+                                       /*IsTypename=*/true,
                                        /*IdentifierInfo=*/nullptr,
                                        /*OnlyNamespace=*/false,
                                        /*InUsingDeclaration=*/true)) {



More information about the cfe-commits mailing list