diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/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(TypedefEnumLookup.getFoundDecl())) { + const Type *UnderlyingTypePtr = + TD->getUnderlyingType().getTypePtrOrNull(); + + if (UnderlyingTypePtr) { + if (EnumDecl *UnderlyingEnumDecl = + dyn_cast(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(PrevDecl)) { if (const TagType *TT = TD->getUnderlyingType()->getAs()) { 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); diff --git a/clang/test/Sema/enum-typedef-msvc.c b/clang/test/Sema/enum-typedef-msvc.c new file mode 100644 --- /dev/null +++ b/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; +} diff --git a/clang/test/Sema/enum-typedef-msvc.cpp b/clang/test/Sema/enum-typedef-msvc.cpp new file mode 100644 --- /dev/null +++ b/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