[clang] d6425e2 - Properly implement 'enum class' parsing.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Sun May 10 13:21:17 PDT 2020


Author: Richard Smith
Date: 2020-05-10T13:21:04-07:00
New Revision: d6425e2c14370ecb5e2a4be4cfdef65f8ae69bd0

URL: https://github.com/llvm/llvm-project/commit/d6425e2c14370ecb5e2a4be4cfdef65f8ae69bd0
DIFF: https://github.com/llvm/llvm-project/commit/d6425e2c14370ecb5e2a4be4cfdef65f8ae69bd0.diff

LOG: Properly implement 'enum class' parsing.

The 'class' or 'struct' keyword is only permitted as part of either an
enum definition or a standalone opaque-enum-declaration, not as part of
an elaborated type specifier. We previously failed to diagnose this, and
generally didn't properly implement the restrictions on elaborated type
specifiers for enumeration types.

In passing, also fixed incorrect parsing for enum-bases, which we
previously parsed as a type-name, but are actually a type-specifier-seq.
This matters for cases like 'enum E : int *p;', which is valid as a
Microsoft extension.

Plus some minor parse diagnostic improvements.

Bumped the recently-added ExtWarn for 'enum E : int x;' to be
DefaultError; this is not an intentional extension, so producing an
error by default seems appropriate, but the warning flag to disable it
may still be useful for code written against old Clang. The same
treatment is given here to the diagnostic for 'enum class E x;', which
we similarly have incorrectly accepted for many years. These diagnostics
continue to be suppressed under -fms-extensions and when compiling
Objective-C code. We will need to decide separately whether Objective-C
should follow the C++ rules or the (older) MSVC rules.

Added: 
    

Modified: 
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Parse/Parser.h
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Parse/ParseTentative.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/test/Parser/MicrosoftExtensions.cpp
    clang/test/Parser/cxx0x-ambig.cpp
    clang/test/Parser/cxx0x-decl.cpp
    clang/test/SemaCXX/enum-scoped.cpp
    clang/test/SemaObjC/enum-fixed-type.m
    clang/test/SemaTemplate/instantiate-local-class.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 04014780615b..ac5b9a605589 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -108,7 +108,18 @@ def warn_cxx98_compat_enum_fixed_underlying_type : Warning<
 def ext_enum_base_in_type_specifier : ExtWarn<
   "non-defining declaration of enumeration with a fixed underlying type is "
   "only permitted as a standalone declaration"
-  "%select{|; missing list of enumerators?}0">, InGroup<DiagGroup<"enum-base">>;
+  "%select{|; missing list of enumerators?}0">,
+  InGroup<DiagGroup<"elaborated-enum-base">>, DefaultError;
+def ext_elaborated_enum_class : ExtWarn<
+  "reference to enumeration must use 'enum' not 'enum %select{struct|class}0'">,
+  InGroup<DiagGroup<"elaborated-enum-class">>, DefaultError;
+def err_scoped_enum_missing_identifier : Error<
+  "scoped enumeration requires a name">;
+def ext_scoped_enum : ExtWarn<
+  "scoped enumerations are a C++11 extension">, InGroup<CXX11>;
+def warn_cxx98_compat_scoped_enum : Warning<
+  "scoped enumerations are incompatible with C++98">,
+  InGroup<CXX98Compat>, DefaultIgnore;
 def err_anonymous_enum_bitfield : Error<
   "ISO C++ only allows ':' in member enumeration declaration to introduce "
   "a fixed underlying type, not an anonymous bit-field">;
@@ -893,14 +904,6 @@ def err_access_specifier_interface : Error<
 def err_duplicate_virt_specifier : Error<
   "class member already marked '%0'">;
 
-def err_scoped_enum_missing_identifier : Error<
-  "scoped enumeration requires a name">;
-def ext_scoped_enum : ExtWarn<
-  "scoped enumerations are a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_scoped_enum : Warning<
-  "scoped enumerations are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
-
 def err_expected_parameter_pack : Error<
   "expected the name of a parameter pack">;
 def err_paren_sizeof_parameter_pack : Error<

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5ca0982ce5c3..fb2d8e48fa70 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2387,9 +2387,6 @@ def err_enum_redeclare_fixed_mismatch : Error<
   "enumeration previously declared with %select{non|}0fixed underlying type">;
 def err_enum_redeclare_scoped_mismatch : Error<
   "enumeration previously declared as %select{un|}0scoped">;
-def err_enum_class_reference : Error<
-  "reference to %select{|scoped }0enumeration must use 'enum' "
-  "not 'enum class'">;
 def err_only_enums_have_underlying_types : Error<
   "only enumeration types have underlying types">;
 def err_underlying_type_of_incomplete_enum : Error<

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index cbdf9fede665..b6b161e482ac 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2662,13 +2662,15 @@ class Parser : public CodeCompletionHandler {
       D.takeAttributes(attrs, endLoc);
     }
   }
-  void MaybeParseCXX11Attributes(ParsedAttributes &attrs,
+  bool MaybeParseCXX11Attributes(ParsedAttributes &attrs,
                                  SourceLocation *endLoc = nullptr) {
     if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
       ParsedAttributesWithRange attrsWithRange(AttrFactory);
       ParseCXX11Attributes(attrsWithRange, endLoc);
       attrs.takeAllFrom(attrsWithRange);
+      return true;
     }
+    return false;
   }
   void MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
                                  SourceLocation *endLoc = nullptr,

diff  --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index af5493d91deb..8a0d63f486c3 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4394,7 +4394,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
 ///         ':' type-specifier-seq
 ///
 /// [C++] elaborated-type-specifier:
-/// [C++]   'enum' '::'[opt] nested-name-specifier[opt] identifier
+/// [C++]   'enum' nested-name-specifier[opt] identifier
 ///
 void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
                                 const ParsedTemplateInfo &TemplateInfo,
@@ -4511,7 +4511,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
   TypeResult BaseType;
   SourceRange BaseRange;
 
-  bool CanBeBitfield = getCurScope()->getFlags() & Scope::ClassScope;
+  bool CanBeBitfield = (getCurScope()->getFlags() & Scope::ClassScope) &&
+                       ScopedEnumKWLoc.isInvalid() && Name;
 
   // Parse the fixed underlying type.
   if (Tok.is(tok::colon)) {
@@ -4535,7 +4536,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
     //   the decl-specifier-seq of a member-declaration is parsed as part of
     //   an enum-base.
     //
-    // Other lamguage modes supporting enumerations with fixed underlying types
+    // Other language modes supporting enumerations with fixed underlying types
     // do not have clear rules on this, so we disambiguate to determine whether
     // the tokens form a bit-field width or an enum-base.
 
@@ -4548,8 +4549,16 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
     } else if (CanHaveEnumBase || !ColonIsSacred) {
       SourceLocation ColonLoc = ConsumeToken();
 
-      BaseType = ParseTypeName(&BaseRange);
-      BaseRange.setBegin(ColonLoc);
+      // Parse a type-specifier-seq as a type. We can't just ParseTypeName here,
+      // because under -fms-extensions,
+      //   enum E : int *p;
+      // declares 'enum E : int; E *p;' not 'enum E : int*; E p;'.
+      DeclSpec DS(AttrFactory);
+      ParseSpecifierQualifierList(DS, AS, DeclSpecContext::DSC_type_specifier);
+      Declarator DeclaratorInfo(DS, DeclaratorContext::TypeNameContext);
+      BaseType = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+
+      BaseRange = SourceRange(ColonLoc, DeclaratorInfo.getSourceRange().getEnd());
 
       if (!getLangOpts().ObjC) {
         if (getLangOpts().CPlusPlus11)
@@ -4587,6 +4596,11 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
         << SourceRange(DS.getFriendSpecLoc());
       ConsumeBrace();
       SkipUntil(tok::r_brace, StopAtSemi);
+      // Discard any other definition-only pieces.
+      attrs.clear();
+      ScopedEnumKWLoc = SourceLocation();
+      IsScopedUsingClassTag = false;
+      BaseType = TypeResult();
       TUK = Sema::TUK_Friend;
     } else {
       TUK = Sema::TUK_Definition;
@@ -4595,6 +4609,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
              (Tok.is(tok::semi) ||
               (Tok.isAtStartOfLine() &&
                !isValidAfterTypeSpecifier(CanBeBitfield)))) {
+    // An opaque-enum-declaration is required to be standalone (no preceding or
+    // following tokens in the declaration). Sema enforces this separately by
+    // diagnosing anything else in the DeclSpec.
     TUK = DS.isFriendSpecified() ? Sema::TUK_Friend : Sema::TUK_Declaration;
     if (Tok.isNot(tok::semi)) {
       // A semicolon was missing after this declaration. Diagnose and recover.
@@ -4606,21 +4623,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
     TUK = Sema::TUK_Reference;
   }
 
-  // If this is an elaborated type specifier, and we delayed
-  // diagnostics before, just merge them into the current pool.
+  bool IsElaboratedTypeSpecifier =
+      TUK == Sema::TUK_Reference || TUK == Sema::TUK_Friend;
+
+  // If this is an elaborated type specifier nested in a larger declaration,
+  // and we delayed diagnostics before, just merge them into the current pool.
   if (TUK == Sema::TUK_Reference && shouldDelayDiagsInTag) {
     diagsFromTag.redelay();
   }
 
-  // A C++11 enum-base can only appear as part of an enum definition or an
-  // opaque-enum-declaration. MSVC and ObjC permit an enum-base anywhere.
-  if (BaseType.isUsable() && TUK != Sema::TUK_Definition &&
-      !getLangOpts().ObjC && !getLangOpts().MicrosoftExt &&
-      !(CanBeOpaqueEnumDeclaration && Tok.is(tok::semi))) {
-    Diag(BaseRange.getBegin(), diag::ext_enum_base_in_type_specifier)
-        << (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes) << BaseRange;
-  }
-
   MultiTemplateParamsArg TParams;
   if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
       TUK != Sema::TUK_Reference) {
@@ -4643,9 +4654,6 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
                                      TemplateInfo.TemplateParams->size());
   }
 
-  if (TUK == Sema::TUK_Reference)
-    ProhibitAttributes(attrs);
-
   if (!Name && TUK != Sema::TUK_Definition) {
     Diag(Tok, diag::err_enumerator_unnamed_no_def);
 
@@ -4654,6 +4662,25 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
     return;
   }
 
+  // An elaborated-type-specifier has a much more constrained grammar:
+  //
+  //   'enum' nested-name-specifier[opt] identifier
+  //
+  // If we parsed any other bits, reject them now.
+  //
+  // MSVC and (for now at least) Objective-C permit a full enum-specifier
+  // or opaque-enum-declaration anywhere.
+  if (IsElaboratedTypeSpecifier && !getLangOpts().MicrosoftExt &&
+      !getLangOpts().ObjC) {
+    ProhibitAttributes(attrs);
+    if (BaseType.isUsable())
+      Diag(BaseRange.getBegin(), diag::ext_enum_base_in_type_specifier)
+          << (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes) << BaseRange;
+    else if (ScopedEnumKWLoc.isValid())
+      Diag(ScopedEnumKWLoc, diag::ext_elaborated_enum_class)
+        << FixItHint::CreateRemoval(ScopedEnumKWLoc) << IsScopedUsingClassTag;
+  }
+
   stripTypeAttributesOffDeclSpec(attrs, DS, TUK);
 
   Sema::SkipBodyInfo SkipBody;
@@ -4728,7 +4755,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
     return;
   }
 
-  if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) {
+  if (Tok.is(tok::l_brace) && TUK == Sema::TUK_Definition) {
     Decl *D = SkipBody.CheckSameAsPrevious ? SkipBody.New : TagDecl;
     ParseEnumBody(StartLoc, D);
     if (SkipBody.CheckSameAsPrevious &&

diff  --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 733d309d5ff2..fd868b81fd09 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -432,9 +432,14 @@ bool Parser::isEnumBase(bool AllowSemi) {
   assert(Tok.is(tok::colon) && "should be looking at the ':'");
 
   RevertingTentativeParsingAction PA(*this);
+  // ':'
   ConsumeToken();
 
+  // type-specifier-seq
   bool InvalidAsDeclSpec = false;
+  // FIXME: We could disallow non-type decl-specifiers here, but it makes no
+  // 
diff erence: those specifiers are ill-formed regardless of the
+  // interpretation.
   TPResult R = isCXXDeclarationSpecifier(/*BracedCastResult*/ TPResult::True,
                                          &InvalidAsDeclSpec);
   if (R == TPResult::Ambiguous) {
@@ -449,8 +454,6 @@ bool Parser::isEnumBase(bool AllowSemi) {
       return true;
 
     // A second decl-specifier unambiguously indicatges an enum-base.
-    // The grammar permits an arbitrary type-name here, but we need an
-    // integral type, so no declarator pieces could ever work.
     R = isCXXDeclarationSpecifier(TPResult::True, &InvalidAsDeclSpec);
   }
 

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 3a377adb04f1..73920c03e0d5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15607,16 +15607,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
 
         if (Kind == TTK_Enum && PrevTagDecl->getTagKind() == TTK_Enum) {
           const EnumDecl *PrevEnum = cast<EnumDecl>(PrevTagDecl);
-
-          // If this is an elaborated-type-specifier for a scoped enumeration,
-          // the 'class' keyword is not necessary and not permitted.
-          if (TUK == TUK_Reference || TUK == TUK_Friend) {
-            if (ScopedEnum)
-              Diag(ScopedEnumKWLoc, diag::err_enum_class_reference)
-                << PrevEnum->isScoped()
-                << FixItHint::CreateRemoval(ScopedEnumKWLoc);
+          if (TUK == TUK_Reference || TUK == TUK_Friend)
             return PrevTagDecl;
-          }
 
           QualType EnumUnderlyingTy;
           if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast<TypeSourceInfo*>())

diff  --git a/clang/test/Parser/MicrosoftExtensions.cpp b/clang/test/Parser/MicrosoftExtensions.cpp
index 461f41cff5a8..10eb13a56b00 100644
--- a/clang/test/Parser/MicrosoftExtensions.cpp
+++ b/clang/test/Parser/MicrosoftExtensions.cpp
@@ -443,3 +443,16 @@ namespace pr36638 {
 struct A;
 void (A::*mp1)(int) __unaligned;
 }
+
+namespace enum_class {
+  // MSVC allows opaque-enum-declaration syntax anywhere an
+  // elaborated-type-specifier can appear.
+  // FIXME: Most of these are missing warnings.
+  enum E0 *p0; // expected-warning {{Microsoft extension}}
+  enum class E1 : int *p1;
+  enum E2 : int *p2;
+  enum class E3 *p3;
+  auto f4() -> enum class E4 { return {}; }
+  auto f5() -> enum E5 : int { return {}; } // FIXME: MSVC rejects this and crashes if the body is {}.
+  auto f6() -> enum E6 { return {}; } // expected-warning {{Microsoft extension}}
+}

diff  --git a/clang/test/Parser/cxx0x-ambig.cpp b/clang/test/Parser/cxx0x-ambig.cpp
index 60a5c32d319a..b4f066b04a5f 100644
--- a/clang/test/Parser/cxx0x-ambig.cpp
+++ b/clang/test/Parser/cxx0x-ambig.cpp
@@ -81,6 +81,12 @@ namespace bitfield {
   struct S8 {
     enum E : int { a = id(U()) }; // expected-error {{no viable conversion}}
   };
+
+  // PR26249: Disambiguate 'enum :' as an enum-base always, even if that would
+  // be ill-formed. It cannot be an elaborated-type-specifier.
+  struct S {
+    enum : undeclared_type { v = 0 }; // expected-error {{unknown type name 'undeclared_type'}}
+  };
 }
 
 namespace trailing_return {

diff  --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp
index de6dfae07941..4ddcb8ccfd0f 100644
--- a/clang/test/Parser/cxx0x-decl.cpp
+++ b/clang/test/Parser/cxx0x-decl.cpp
@@ -44,18 +44,18 @@ namespace OpaqueEnumDecl {
   int x[sizeof(enum E : int)]; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration}}
 
   namespace PR24297 {
-    enum struct E a; // expected-error {{must use 'enum' not 'enum class'}} FIXME: we used 'enum struct'
-    enum class F b; // FIXME: invalid, no prior declaration of 'enum F' and in any case we cannot use 'class' here
+    enum struct E a; // expected-error {{must use 'enum' not 'enum struct'}}
+    enum class F b; // expected-error {{must use 'enum' not 'enum class'}}
     enum G : int c; // expected-error {{only permitted as a standalone declaration}}
     enum struct H : int d; // expected-error {{only permitted as a standalone declaration}}
     enum class I : int e; // expected-error {{only permitted as a standalone declaration}}
     enum X x; // expected-error {{ISO C++ forbids forward reference}} expected-error {{incomplete}} expected-note {{forward declaration}}
 
-    enum struct E *pa; // expected-error {{must use 'enum' not 'enum class'}} FIXME: we used 'enum struct'
+    enum struct E *pa; // expected-error {{must use 'enum' not 'enum struct'}}
     enum class F *pb; // expected-error {{must use 'enum' not 'enum class'}}
-    enum G : int *pc; // expected-error {{only permitted as a standalone declaration}} expected-error {{'int *' is an invalid underlying type}}
-    enum struct H : int *pd; // expected-error {{only permitted as a standalone declaration}} expected-error {{'int *' is an invalid underlying type}} FIXME: expected-error {{must use 'enum' not 'enum class'}}
-    enum class I : int *pe; // expected-error {{only permitted as a standalone declaration}} expected-error {{'int *' is an invalid underlying type}} FIXME: expected-error {{must use 'enum' not 'enum class'}}
+    enum G : int *pc; // expected-error {{only permitted as a standalone declaration}}
+    enum struct H : int *pd; // expected-error {{only permitted as a standalone declaration}}
+    enum class I : int *pe; // expected-error {{only permitted as a standalone declaration}}
     enum Y *py; // expected-error {{ISO C++ forbids forward reference}}
   }
 }

diff  --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp
index 34707b894488..10403bfa5125 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -102,7 +102,6 @@ enum : long {
 };
 
 enum : long x; // expected-error{{unnamed enumeration must be a definition}} \
-// expected-warning{{only permitted as a standalone declaration}} \
 // expected-warning{{declaration does not declare anything}}
 
 void PR9333() {
@@ -150,20 +149,23 @@ namespace PR11484 {
 }
 
 namespace N2764 {
+  enum class E *x0a; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
+  enum E2 *x0b; // OK
   enum class E { a, b };
   enum E x1 = E::a; // ok
-  enum class E x2 = E::a; // expected-error {{reference to scoped enumeration must use 'enum' not 'enum class'}}
+  enum class E x2 = E::a; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
 
   enum F { a, b };
   enum F y1 = a; // ok
   enum class F y2 = a; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
 
   struct S {
-    friend enum class E; // expected-error {{reference to scoped enumeration must use 'enum' not 'enum class'}}
+    friend enum class E; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
     friend enum class F; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
 
     friend enum G {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
-    friend enum class H {}; // expected-error {{cannot define a type in a friend declaration}}
+    friend enum class H {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
+    friend enum I : int {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
 
     enum A : int;
     A a;

diff  --git a/clang/test/SemaObjC/enum-fixed-type.m b/clang/test/SemaObjC/enum-fixed-type.m
index d991ac3e2d08..42308b75ca9a 100644
--- a/clang/test/SemaObjC/enum-fixed-type.m
+++ b/clang/test/SemaObjC/enum-fixed-type.m
@@ -23,8 +23,8 @@
 struct X { 
   enum Color : 4;
   enum Color field1: 4;
-  enum Other : Integer field2; // c-warning {{only permitted as a standalone}}
-  enum Other : Integer field3 : 4; // c-warning {{only permitted as a standalone}}
+  enum Other : Integer field2; // c-error {{only permitted as a standalone}}
+  enum Other : Integer field3 : 4; // c-error {{only permitted as a standalone}}
   enum  : Integer { Blah, Blarg } field4 : 4;
 };
 

diff  --git a/clang/test/SemaTemplate/instantiate-local-class.cpp b/clang/test/SemaTemplate/instantiate-local-class.cpp
index 550c59d61778..e10065c8ea40 100644
--- a/clang/test/SemaTemplate/instantiate-local-class.cpp
+++ b/clang/test/SemaTemplate/instantiate-local-class.cpp
@@ -238,7 +238,8 @@ namespace PR18653 {
   template void f2<int>();
 
   template<typename T> void f3() {
-    void g3(enum class x3);
+    enum class x3;
+    void g3(enum x3);
     enum class x3 { nothing };
   }
   template void f3<int>();
@@ -273,7 +274,8 @@ namespace PR18653 {
 
   template <class T> struct S3 {
     void m() {
-      f<enum class new_enum>();
+      enum class new_enum;
+      f<enum new_enum>();
     }
   };
   template struct S3<int>;


        


More information about the cfe-commits mailing list