aaron.ballman created this revision.
aaron.ballman added reviewers: rsmith, dblaikie.

In C, enumerators are not hoisted into, say, a struct decl context when the 
enumeration is declared inside of a struct. Instead, the enumerators are 
hoisted into the translation unit decl context. This patch fixes 
`getRedeclContext()` to skip records as well as transparent contexts when the 
original context is an enumeration. This allows us to catch enumerator 
redeclarations as well as silent name hiding + miscompiles.

This patch address PR15071.


https://reviews.llvm.org/D52384

Files:
  lib/AST/DeclBase.cpp
  test/Sema/enum.c


Index: test/Sema/enum.c
===================================================================
--- test/Sema/enum.c
+++ test/Sema/enum.c
@@ -135,3 +135,26 @@
   };
   int makeStructNonEmpty;
 };
+
+static int EnumRedecl; // expected-note 2 {{previous definition is here}}
+struct S {
+  enum {
+    EnumRedecl = 4 // expected-error {{redefinition of 'EnumRedecl'}}
+  } e;
+};
+
+union U {
+  enum {
+    EnumRedecl = 5 // expected-error {{redefinition of 'EnumRedecl'}}
+  } e;
+};
+
+enum PR15071 {
+  PR15071_One // expected-note {{previous definition is here}}
+};
+
+struct EnumRedeclStruct {
+  enum {
+    PR15071_One // expected-error {{redefinition of enumerator 'PR15071_One'}}
+  } e;
+};
Index: lib/AST/DeclBase.cpp
===================================================================
--- lib/AST/DeclBase.cpp
+++ lib/AST/DeclBase.cpp
@@ -1700,8 +1700,16 @@
 
 DeclContext *DeclContext::getRedeclContext() {
   DeclContext *Ctx = this;
-  // Skip through transparent contexts.
-  while (Ctx->isTransparentContext())
+
+  // In C, the redeclaration context for enumerators is the translation unit,
+  // so we skip through transparent contexts as well as struct/union contexts.
+  bool SkipRecords = getDeclKind() == Decl::Kind::Enum &&
+                     !getParentASTContext().getLangOpts().CPlusPlus;
+
+  // Skip through contexts to get to the redeclaration context. Transparent
+  // contexts are always skipped.
+  while (SkipRecords ? Ctx->isTransparentContext() || Ctx->isRecord()
+                     : Ctx->isTransparentContext())
     Ctx = Ctx->getParent();
   return Ctx;
 }


Index: test/Sema/enum.c
===================================================================
--- test/Sema/enum.c
+++ test/Sema/enum.c
@@ -135,3 +135,26 @@
   };
   int makeStructNonEmpty;
 };
+
+static int EnumRedecl; // expected-note 2 {{previous definition is here}}
+struct S {
+  enum {
+    EnumRedecl = 4 // expected-error {{redefinition of 'EnumRedecl'}}
+  } e;
+};
+
+union U {
+  enum {
+    EnumRedecl = 5 // expected-error {{redefinition of 'EnumRedecl'}}
+  } e;
+};
+
+enum PR15071 {
+  PR15071_One // expected-note {{previous definition is here}}
+};
+
+struct EnumRedeclStruct {
+  enum {
+    PR15071_One // expected-error {{redefinition of enumerator 'PR15071_One'}}
+  } e;
+};
Index: lib/AST/DeclBase.cpp
===================================================================
--- lib/AST/DeclBase.cpp
+++ lib/AST/DeclBase.cpp
@@ -1700,8 +1700,16 @@
 
 DeclContext *DeclContext::getRedeclContext() {
   DeclContext *Ctx = this;
-  // Skip through transparent contexts.
-  while (Ctx->isTransparentContext())
+
+  // In C, the redeclaration context for enumerators is the translation unit,
+  // so we skip through transparent contexts as well as struct/union contexts.
+  bool SkipRecords = getDeclKind() == Decl::Kind::Enum &&
+                     !getParentASTContext().getLangOpts().CPlusPlus;
+
+  // Skip through contexts to get to the redeclaration context. Transparent
+  // contexts are always skipped.
+  while (SkipRecords ? Ctx->isTransparentContext() || Ctx->isRecord()
+                     : Ctx->isTransparentContext())
     Ctx = Ctx->getParent();
   return Ctx;
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to