Updated patch according to reviewer's notes. Added new tests.

Hi rsmith,

http://llvm-reviews.chandlerc.com/D2151

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D2151?vs=5468&id=5513#toc

Files:
  include/clang/AST/DeclBase.h
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/Decl.cpp
  lib/AST/DeclBase.cpp
  lib/Sema/SemaDecl.cpp
  test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
  test/SemaCXX/extern-c.cpp
  test/SemaCXX/storage-class.cpp
Index: include/clang/AST/DeclBase.h
===================================================================
--- include/clang/AST/DeclBase.h
+++ include/clang/AST/DeclBase.h
@@ -1159,6 +1159,14 @@
   /// C++0x scoped enums), and C++ linkage specifications.
   bool isTransparentContext() const;
 
+  /// \brief Determines whether this context or some of its ancestors is a
+  /// linkage specification context that specifies C linkage.
+  bool isExternCContext() const;
+
+  /// \brief Determines whether this context or some of its ancestors is a
+  /// linkage specification context that specifies C++ linkage.
+  bool isExternCXXContext() const;
+
   /// \brief Determine whether this declaration context is equivalent
   /// to the declaration context DC.
   bool Equals(const DeclContext *DC) const {
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -49,6 +49,7 @@
 def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">;
 def C99Compat : DiagGroup<"c99-compat">;
 def CXXCompat: DiagGroup<"c++-compat">;
+def ExternCCompat : DiagGroup<"extern-c-compat">;
 def GNUCaseRange : DiagGroup<"gnu-case-range">;
 def CastAlign : DiagGroup<"cast-align">;
 def : DiagGroup<"cast-qual">;
@@ -494,7 +495,8 @@
     ObjCMissingSuperCalls,
     OverloadedVirtual,
     PrivateExtern,
-    SelTypeCast
+    SelTypeCast,
+    ExternCCompat
  ]>;
 
 // Thread Safety warnings 
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -5718,6 +5718,9 @@
 def warn_zero_size_struct_union_compat : Warning<"%select{|empty }0"
   "%select{struct|union}1 has size 0 in C, %select{size 1|non-zero size}2 in C++">,
   InGroup<CXXCompat>, DefaultIgnore;
+def warn_zero_size_struct_union_in_extern_c : Warning<"%select{|empty }0"
+  "%select{struct|union}1 has size 0 in C, %select{size 1|non-zero size}2 in C++">,
+  InGroup<ExternCCompat>;
 } // End of general sema category.
 
 // inline asm.
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -1698,27 +1698,12 @@
   return isExternCTemplate(*this);
 }
 
-static bool isLinkageSpecContext(const DeclContext *DC,
-                                 LinkageSpecDecl::LanguageIDs ID) {
-  while (DC->getDeclKind() != Decl::TranslationUnit) {
-    if (DC->getDeclKind() == Decl::LinkageSpec)
-      return cast<LinkageSpecDecl>(DC)->getLanguage() == ID;
-    DC = DC->getParent();
-  }
-  return false;
-}
-
-template <typename T>
-static bool isInLanguageSpecContext(T *D, LinkageSpecDecl::LanguageIDs ID) {
-  return isLinkageSpecContext(D->getLexicalDeclContext(), ID);
-}
-
 bool VarDecl::isInExternCContext() const {
-  return isInLanguageSpecContext(this, LinkageSpecDecl::lang_c);
+  return getLexicalDeclContext()->isExternCContext();
 }
 
 bool VarDecl::isInExternCXXContext() const {
-  return isInLanguageSpecContext(this, LinkageSpecDecl::lang_cxx);
+  return getLexicalDeclContext()->isExternCXXContext();
 }
 
 VarDecl *VarDecl::getCanonicalDecl() { return getFirstDecl(); }
@@ -2407,11 +2392,11 @@
 }
 
 bool FunctionDecl::isInExternCContext() const {
-  return isInLanguageSpecContext(this, LinkageSpecDecl::lang_c);
+  return getLexicalDeclContext()->isExternCContext();
 }
 
 bool FunctionDecl::isInExternCXXContext() const {
-  return isInLanguageSpecContext(this, LinkageSpecDecl::lang_cxx);
+  return getLexicalDeclContext()->isExternCXXContext();
 }
 
 bool FunctionDecl::isGlobal() const {
Index: lib/AST/DeclBase.cpp
===================================================================
--- lib/AST/DeclBase.cpp
+++ lib/AST/DeclBase.cpp
@@ -806,6 +806,24 @@
   return false;
 }
 
+static bool isLinkageSpecContext(const DeclContext *DC,
+                                 LinkageSpecDecl::LanguageIDs ID) {
+  while (DC->getDeclKind() != Decl::TranslationUnit) {
+    if (DC->getDeclKind() == Decl::LinkageSpec)
+      return cast<LinkageSpecDecl>(DC)->getLanguage() == ID;
+    DC = DC->getParent();
+  }
+  return false;
+}
+
+bool DeclContext::isExternCContext() const {
+  return isLinkageSpecContext(this, clang::LinkageSpecDecl::lang_c);
+}
+
+bool DeclContext::isExternCXXContext() const {
+  return isLinkageSpecContext(this, clang::LinkageSpecDecl::lang_cxx);
+}
+
 bool DeclContext::Encloses(const DeclContext *DC) const {
   if (getPrimaryContext() != this)
     return getPrimaryContext()->Encloses(DC);
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -12063,8 +12063,21 @@
     if (Record->hasAttrs())
       CheckAlignasUnderalignment(Record);
 
-    // Check if the structure/union declaration is a language extension.
+    // Check if the structure/union declaration is a type that can have zero
+    // size in C. For C this is a language extension, for C++ it may cause
+    // compatibility problems.
+    bool CheckForZeroSize;
     if (!getLangOpts().CPlusPlus) {
+      CheckForZeroSize = true;
+    } else {
+      // For C++ filter out types that cannot be referenced in C code.
+      CXXRecordDecl *CXXRecord = cast<CXXRecordDecl>(Record);
+      CheckForZeroSize =
+          CXXRecord->getLexicalDeclContext()->isExternCContext() &&
+          !CXXRecord->isDependentType() &&
+          CXXRecord->isCLike();
+    }
+    if (CheckForZeroSize) {
       bool ZeroSize = true;
       bool IsEmpty = true;
       unsigned NonBitFields = 0;
@@ -12084,19 +12097,22 @@
         }
       }
 
-      // Empty structs are an extension in C (C99 6.7.2.1p7), but are allowed in
-      // C++.
-      if (ZeroSize)
-        Diag(RecLoc, diag::warn_zero_size_struct_union_compat) << IsEmpty
-            << Record->isUnion() << (NonBitFields > 1);
+      // Empty structs are an extension in C (C99 6.7.2.1p7). They are
+      // allowed in C++, but warn if its declaration is inside
+      // extern "C" block.
+      if (ZeroSize) {
+        Diag(RecLoc, getLangOpts().CPlusPlus ?
+                         diag::warn_zero_size_struct_union_in_extern_c :
+                         diag::warn_zero_size_struct_union_compat)
+          << IsEmpty << Record->isUnion() << (NonBitFields > 1);
+      }
 
-      // Structs without named members are extension in C (C99 6.7.2.1p7), but
-      // are accepted by GCC.
-      if (NonBitFields == 0) {
-        if (IsEmpty)
-          Diag(RecLoc, diag::ext_empty_struct_union) << Record->isUnion();
-        else
-          Diag(RecLoc, diag::ext_no_named_members_in_struct_union) << Record->isUnion();
+      // Structs without named members are extension in C (C99 6.7.2.1p7),
+      // but are accepted by GCC.
+      if (NonBitFields == 0 && !getLangOpts().CPlusPlus) {
+        Diag(RecLoc, IsEmpty ? diag::ext_empty_struct_union :
+                               diag::ext_no_named_members_in_struct_union)
+          << Record->isUnion();
       }
     }
   } else {
Index: test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
===================================================================
--- test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
+++ test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
@@ -72,7 +72,7 @@
 }
 
 extern "C" {
-  struct L { };
+  struct L { int x; };
 }
 
 void h(L); // expected-note{{candidate function}}
Index: test/SemaCXX/extern-c.cpp
===================================================================
--- test/SemaCXX/extern-c.cpp
+++ test/SemaCXX/extern-c.cpp
@@ -140,7 +140,7 @@
 
 // We allow all these even though the standard says they are ill-formed.
 extern "C" {
-  struct stat {};
+  struct stat {};   // expected-warning{{empty struct has size 0 in C, size 1 in C++}}
   void stat(struct stat);
 }
 namespace X {
@@ -187,3 +187,20 @@
   extern "C" double global_var_vs_extern_c_var_2; // expected-note {{here}}
 }
 int global_var_vs_extern_c_var_2; // expected-error {{conflicts with declaration with C language linkage}}
+
+template <class T> struct pr5065_n1 {};
+extern "C" {
+  union pr5065_1 {}; // expected-warning{{empty union has size 0 in C, size 1 in C++}}
+  struct pr5065_2 { int: 0; }; // expected-warning{{struct has size 0 in C, size 1 in C++}}
+  struct pr5065_3 {}; // expected-warning{{empty struct has size 0 in C, size 1 in C++}}
+  struct pr5065_4 { // expected-warning{{empty struct has size 0 in C, size 1 in C++}}
+    struct Inner {}; // expected-warning{{empty struct has size 0 in C, size 1 in C++}}
+  };
+  // These should not warn
+  class pr5065_n3 {};
+  pr5065_n1<int> pr5065_v;
+  struct pr5065_n4 { void m() {} };
+  struct pr5065_n5 : public pr5065_3 {};
+  struct pr5065_n6 : public virtual pr5065_3 {};
+}
+struct pr5065_n7 {};
Index: test/SemaCXX/storage-class.cpp
===================================================================
--- test/SemaCXX/storage-class.cpp
+++ test/SemaCXX/storage-class.cpp
@@ -4,4 +4,4 @@
 extern const int PR6495c[] = {42,43,44};
 
 extern struct Test1 {}; // expected-warning {{'extern' is not permitted on a declaration of a type}}
-extern "C" struct Test0 {}; // no warning
+extern "C" struct Test0 { int x; }; // no warning
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to