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, 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; } // 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(DC)->getLanguage() == ID; - DC = DC->getParent(); - } - return false; -} - -template -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(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(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