sepavloff added you to the CC list for the revision "Warn on zero-sized
structures inside extern "C" block (PR5065)".
Hi rsmith,
This patch adds warning on structures/unions that are empty or contain
only bit fields of zero size. Warnings are generated in C++ mode and
if only such type is defined inside extern "C" block.
The patch fixed PR5065.
http://llvm-reviews.chandlerc.com/D2151
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 : 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,30 @@
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.
+ if (Record->getLexicalDeclContext()->isExternCContext() &&
+ !Record->isDependentType()) {
+ if (CXXRecordDecl *CXXRec = dyn_cast<CXXRecordDecl>(Record)) {
+ CheckForZeroSize =
+ (CXXRec->method_begin() == CXXRec->method_end()) &&
+ CXXRec->getNumBases() == 0 &&
+ CXXRec->getNumVBases() == 0 &&
+ (CXXRec->getTagKind() == TTK_Struct ||
+ CXXRec->getTagKind() == TTK_Union) &&
+ CXXRec->getTemplateSpecializationKind() == TSK_Undeclared;
+ } else {
+ CheckForZeroSize = true;
+ }
+ }
+ }
+ if (CheckForZeroSize) {
bool ZeroSize = true;
bool IsEmpty = true;
unsigned NonBitFields = 0;
@@ -12079,24 +12101,34 @@
++NonBitFields;
QualType FieldType = I->getType();
if (FieldType->isIncompleteType() ||
+ FieldType->isDependentType() ||
+ FieldType->isUndeducedType() ||
!Context.getTypeSizeInChars(FieldType).isZero())
ZeroSize = false;
}
}
- // 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);
+ if (!getLangOpts().CPlusPlus) {
+ // 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);
- // 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) {
+ if (IsEmpty)
+ Diag(RecLoc, diag::ext_empty_struct_union) << Record->isUnion();
+ else
+ Diag(RecLoc, diag::ext_no_named_members_in_struct_union)
+ << Record->isUnion();
+ }
+ } else if (ZeroSize) {
+ // Empty structs are OK in C++ but will warn if its declaration is inside
+ // extern "C" block.
+ Diag(RecLoc, diag::warn_zero_size_struct_union) << IsEmpty
+ << Record->isUnion() << (NonBitFields > 1);
}
}
} 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,8 @@
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}}
+
+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++}}
+}
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