[PATCH] D91659: Allow anonymous enum typedefs to be referenced with the 'enum' specifier under MSVC compat mode

Shivanshu Goyal via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 17 14:09:34 PST 2020


shivanshu3 created this revision.
shivanshu3 added reviewers: zahen, tiagoma, rnk, hans.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
shivanshu3 requested review of this revision.

Goal: Clang should be able to parse the following code with '-fms-compatibility' because MSVC allows using the 'enum' specifier for typedef'd anonymous enums:

  typedef enum
  {
      First,
      Second
  } MyEnum;
  
  void func()
  {
      enum MyEnum foo;
  }

Today the code above produces the following compile error:

  <source>:9:7: error: typedef 'MyEnum' cannot be referenced with a enum specifier
          enum MyEnum foo;
               ^
  <source>:5:3: note: declared here
  } MyEnum;
    ^

The reason why this change is desired in Clang is because the MSVC tools 'mktyplib' and 'midl' produce C++ code which uses the 'enum' specifier in such a way. Here is a small repro:

MyEnum.idl:

  [
        uuid(da2889bf-6173-4c1c-a23d-52ad85fc91ca)
  ]
  library MyEnumLib
  {
        typedef enum { X } MyEnum;
  };

Application.idl:

  [
        uuid(4d0cc96f-3258-46f7-98bf-6654c1d027c5)
  ]
  library ApplicationLib
  {
        importlib("MyEnum.tlb");
  
        [
              uuid(6ac4c3d7-15c7-40c7-8b64-60459e29680b)
        ]
        interface ApplicationInterface : IDispatch
        {
              [
                    id(66),
                    propget
              ]
              HRESULT Foo([out, retval] enum MyEnum* Foo);
  
              [
                    id(66),
                    propput
              ]
              HRESULT Foo([in] enum MyEnum Foo);
        };
  };

Use the following commands to build:

  mktyplib /win32 /cpp_cmd cl.exe /cpp_opt /E /tlb MyEnum.tlb /h MyEnum.h MyEnum.idl
  midl -h Application.h -iid Application.c -tlb Application.tlb /cpp_cmd cl.exe Application.idl

This produces Application.h with the following snippet (note the usage of the 'enum' specifier):

  MIDL_INTERFACE("6ac4c3d7-15c7-40c7-8b64-60459e29680b")
  ApplicationInterface : public IDispatch
  {
  public:
      virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Foo( 
          /* [retval][out] */ enum /* external definition not present */ MyEnum *Foo) = 0;
      
      virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Foo( 
          /* [in] */ enum /* external definition not present */ MyEnum Foo) = 0;
      
  };

Note that the 'mktyplib' tool is deprecated now but it's used for building some code here at Microsoft, and probably other legacy build scenarios in the industry as well.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D91659

Files:
  clang/lib/Sema/SemaDecl.cpp
  clang/test/Sema/enum-typedef-msvc.c
  clang/test/Sema/enum-typedef-msvc.cpp


Index: clang/test/Sema/enum-typedef-msvc.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/enum-typedef-msvc.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility %s
+
+typedef enum {
+  First,
+  Second
+} MyEnum;
+
+class AMyInterface {
+  virtual void MyFunc(enum MyEnum *param) = 0;
+};
+
+class MyImpl : public AMyInterface {
+  virtual void MyFunc(enum MyEnum *param) override {}
+};
+
+// expected-no-diagnostics
Index: clang/test/Sema/enum-typedef-msvc.c
===================================================================
--- /dev/null
+++ clang/test/Sema/enum-typedef-msvc.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -DUSE_MSVC_COMPAT %s
+
+typedef enum {
+  A
+} Foo;
+
+void func() {
+#ifdef USE_MSVC_COMPAT
+  enum Foo foo; // expected-no-diagnostics
+#else
+  enum Foo foo; // expected-error {{variable has incomplete type 'enum Foo'}} // expected-note {{forward declaration of 'enum Foo'}}
+#endif
+  (void)foo;
+}
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -15539,6 +15539,34 @@
     // shouldn't be diagnosing.
     LookupName(Previous, S);
 
+    // Under MSVC, the 'enum' specifier can be used for typedef'd enums.
+    // Note that lookup only fails in C, not C++, so this if condition
+    // is only used for C code.
+    if (getLangOpts().MSVCCompat && (Kind == TTK_Enum) && Previous.empty() &&
+        (TUK == TUK_Reference)) {
+      LookupResult TypedefEnumLookup(*this, Name, NameLoc, LookupOrdinaryName,
+                                     Redecl);
+      LookupName(TypedefEnumLookup, S);
+
+      if (!TypedefEnumLookup.empty()) {
+        if (TypedefNameDecl *TD =
+                dyn_cast<TypedefNameDecl>(TypedefEnumLookup.getFoundDecl())) {
+          const Type *UnderlyingTypePtr =
+              TD->getUnderlyingType().getTypePtrOrNull();
+
+          if (UnderlyingTypePtr) {
+            if (EnumDecl *UnderlyingEnumDecl =
+                    dyn_cast<EnumDecl>(UnderlyingTypePtr->getAsTagDecl())) {
+              // We only allow this for anonymous enums
+              if (UnderlyingEnumDecl->getDeclName().isEmpty()) {
+                Previous.addDecl(UnderlyingEnumDecl);
+              }
+            }
+          }
+        }
+      }
+    }
+
     // When declaring or defining a tag, ignore ambiguities introduced
     // by types using'ed into this scope.
     if (Previous.isAmbiguous() &&
@@ -15721,9 +15749,12 @@
       if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(PrevDecl)) {
         if (const TagType *TT = TD->getUnderlyingType()->getAs<TagType>()) {
           TagDecl *Tag = TT->getDecl();
-          if (Tag->getDeclName() == Name &&
-              Tag->getDeclContext()->getRedeclContext()
-                          ->Equals(TD->getDeclContext()->getRedeclContext())) {
+          bool AnonymousEnumEligible = getLangOpts().MSVCCompat &&
+                                       (Kind == TTK_Enum) &&
+                                       Tag->getDeclName().isEmpty();
+          if ((Tag->getDeclName() == Name || AnonymousEnumEligible) &&
+              Tag->getDeclContext()->getRedeclContext()->Equals(
+                  TD->getDeclContext()->getRedeclContext())) {
             PrevDecl = Tag;
             Previous.clear();
             Previous.addDecl(Tag);


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D91659.305903.patch
Type: text/x-patch
Size: 3536 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20201117/bc5497be/attachment-0001.bin>


More information about the cfe-commits mailing list