Index: include/clang/AST/ASTStructuralEquivalence.h =================================================================== --- include/clang/AST/ASTStructuralEquivalence.h +++ include/clang/AST/ASTStructuralEquivalence.h @@ -62,9 +62,11 @@ StructuralEquivalenceContext( ASTContext &FromCtx, ASTContext &ToCtx, llvm::DenseSet> &NonEquivalentDecls, - bool StrictTypeSpelling = false, bool Complain = true) + bool StrictTypeSpelling = false, bool Complain = true, + bool ErrorOnTagTypeMismatch = false) : FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls), - StrictTypeSpelling(StrictTypeSpelling), Complain(Complain), + StrictTypeSpelling(StrictTypeSpelling), + ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain), LastDiagFromC2(false) {} DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID); Index: include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- include/clang/Basic/DiagnosticASTKinds.td +++ include/clang/Basic/DiagnosticASTKinds.td @@ -200,12 +200,17 @@ def err_odr_function_type_inconsistent : Error< "external function %0 declared with incompatible types in different " "translation units (%1 vs. %2)">; -def warn_odr_tag_type_inconsistent : Warning< - "type %0 has incompatible definitions in different translation units">, - InGroup>; +def warn_odr_tag_type_inconsistent + : Warning<"type %0 has incompatible definitions in different translation " + "units">, + InGroup>; +def err_odr_tag_type_inconsistent + : Error<"type %0 has incompatible definitions in different translation " + "units">; def note_odr_tag_kind_here: Note< "%0 is a %select{struct|interface|union|class|enum}1 here">; def note_odr_field : Note<"field %0 has type %1 here">; +def note_odr_field_name : Note<"field has name %0 here">; def note_odr_missing_field : Note<"no corresponding field here">; def note_odr_bit_field : Note<"bit-field %0 with type %1 and length %2 here">; def note_odr_not_bit_field : Note<"field %0 is not a bit-field">; Index: include/clang/Basic/Visibility.h =================================================================== --- include/clang/Basic/Visibility.h +++ include/clang/Basic/Visibility.h @@ -75,6 +75,9 @@ static LinkageInfo none() { return LinkageInfo(NoLinkage, DefaultVisibility, false); } + static LinkageInfo visible_none() { + return LinkageInfo(VisibleNoLinkage, DefaultVisibility, false); + } Linkage getLinkage() const { return (Linkage)linkage_; } Visibility getVisibility() const { return (Visibility)visibility_; } Index: include/clang/Sema/Lookup.h =================================================================== --- include/clang/Sema/Lookup.h +++ include/clang/Sema/Lookup.h @@ -273,7 +273,7 @@ /// declarations, such as those in modules that have not yet been imported. bool isHiddenDeclarationVisible(NamedDecl *ND) const { return AllowHidden || - (isForRedeclaration() && ND->isExternallyVisible()); + (isForRedeclaration() && ND->hasExternalFormalLinkage()); } /// Sets whether tag declarations should be hidden by non-tag Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1542,6 +1542,10 @@ bool hasVisibleMergedDefinition(NamedDecl *Def); + /// Determine if \p D and \p Suggested have a structurally compatible + /// layout as described in C11 6.2.7/1. + bool hasStructuralCompatLayout(Decl *D, Decl *Suggested); + /// Determine if \p D has a visible definition. If not, suggest a declaration /// that should be made visible to expose the definition. bool hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested, @@ -1629,9 +1633,13 @@ // struct SkipBodyInfo { - SkipBodyInfo() : ShouldSkip(false), Previous(nullptr) {} + SkipBodyInfo() + : ShouldSkip(false), CheckSameAsPrevious(false), Previous(nullptr), + New(nullptr) {} bool ShouldSkip; + bool CheckSameAsPrevious; NamedDecl *Previous; + NamedDecl *New; }; DeclGroupPtrTy ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType = nullptr); @@ -2145,13 +2153,11 @@ }; Decl *ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, - SourceLocation KWLoc, CXXScopeSpec &SS, - IdentifierInfo *Name, SourceLocation NameLoc, - AttributeList *Attr, AccessSpecifier AS, - SourceLocation ModulePrivateLoc, - MultiTemplateParamsArg TemplateParameterLists, - bool &OwnedDecl, bool &IsDependent, - SourceLocation ScopedEnumKWLoc, + SourceLocation KWLoc, CXXScopeSpec &SS, IdentifierInfo *Name, + SourceLocation NameLoc, AttributeList *Attr, + AccessSpecifier AS, SourceLocation ModulePrivateLoc, + MultiTemplateParamsArg TemplateParameterLists, bool &OwnedDecl, + bool &IsDependent, SourceLocation ScopedEnumKWLoc, bool ScopedEnumUsesClassTag, TypeResult UnderlyingType, bool IsTypeSpecifier, bool IsTemplateParamOrArg, SkipBodyInfo *SkipBody = nullptr); @@ -2219,6 +2225,12 @@ /// struct, or union). void ActOnTagStartDefinition(Scope *S, Decl *TagDecl); + /// Perform ODR-like check for C/ObjC when merging tag types from modules. + /// Differently from C++, actually parse the body and reject / error out + /// in case of a structural mismatch. + bool ActOnDuplicateDefinition(DeclSpec &DS, Decl *Prev, + SkipBodyInfo &SkipBody); + typedef void *SkippedDefinitionContext; /// \brief Invoked when we enter a tag definition that we're skipping. @@ -2272,8 +2284,8 @@ Decl *ActOnEnumConstant(Scope *S, Decl *EnumDecl, Decl *LastEnumConstant, SourceLocation IdLoc, IdentifierInfo *Id, - AttributeList *Attrs, - SourceLocation EqualLoc, Expr *Val); + AttributeList *Attrs, SourceLocation EqualLoc, + Expr *Val); void ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange, Decl *EnumDecl, ArrayRef Elements, Index: lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- lib/AST/ASTStructuralEquivalence.cpp +++ lib/AST/ASTStructuralEquivalence.cpp @@ -735,13 +735,28 @@ // Check for equivalent field names. IdentifierInfo *Name1 = Field1->getIdentifier(); IdentifierInfo *Name2 = Field2->getIdentifier(); - if (!::IsStructurallyEquivalent(Name1, Name2)) + if (!::IsStructurallyEquivalent(Name1, Name2)) { + if (Context.Complain) { + Context.Diag2(Owner2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(Owner2); + Context.Diag2(Field2->getLocation(), diag::note_odr_field_name) + << Field2->getDeclName(); + Context.Diag1(Field1->getLocation(), diag::note_odr_field_name) + << Field1->getDeclName(); + } return false; + } if (!IsStructurallyEquivalent(Context, Field1->getType(), Field2->getType())) { if (Context.Complain) { - Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent) + Context.Diag2(Owner2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(Owner2); Context.Diag2(Field2->getLocation(), diag::note_odr_field) << Field2->getDeclName() << Field2->getType(); @@ -753,7 +768,10 @@ if (Field1->isBitField() != Field2->isBitField()) { if (Context.Complain) { - Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent) + Context.Diag2(Owner2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(Owner2); if (Field1->isBitField()) { Context.Diag1(Field1->getLocation(), diag::note_odr_bit_field) @@ -780,7 +798,9 @@ if (Bits1 != Bits2) { if (Context.Complain) { Context.Diag2(Owner2->getLocation(), - diag::warn_odr_tag_type_inconsistent) + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(Owner2); Context.Diag2(Field2->getLocation(), diag::note_odr_bit_field) << Field2->getDeclName() << Field2->getType() << Bits2; @@ -799,7 +819,10 @@ RecordDecl *D1, RecordDecl *D2) { if (D1->isUnion() != D2->isUnion()) { if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + Context.Diag2(D2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(D2); Context.Diag1(D1->getLocation(), diag::note_odr_tag_kind_here) << D1->getDeclName() << (unsigned)D1->getTagKind(); @@ -927,7 +950,10 @@ Field1 != Field1End; ++Field1, ++Field2) { if (Field2 == Field2End) { if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + Context.Diag2(D2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(D2); Context.Diag1(Field1->getLocation(), diag::note_odr_field) << Field1->getDeclName() << Field1->getType(); @@ -942,7 +968,10 @@ if (Field2 != Field2End) { if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + Context.Diag2(D2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(D2); Context.Diag2(Field2->getLocation(), diag::note_odr_field) << Field2->getDeclName() << Field2->getType(); @@ -964,7 +993,10 @@ EC1 != EC1End; ++EC1, ++EC2) { if (EC2 == EC2End) { if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + Context.Diag2(D2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(D2); Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator) << EC1->getDeclName() << EC1->getInitVal().toString(10); @@ -978,7 +1010,10 @@ if (!llvm::APSInt::isSameValue(Val1, Val2) || !IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) { if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + Context.Diag2(D2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(D2); Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator) << EC2->getDeclName() << EC2->getInitVal().toString(10); @@ -991,7 +1026,10 @@ if (EC2 != EC2End) { if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + Context.Diag2(D2->getLocation(), + Context.ErrorOnTagTypeMismatch + ? diag::err_odr_tag_type_inconsistent + : diag::warn_odr_tag_type_inconsistent) << Context.ToCtx.getTypeDeclType(D2); Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator) << EC2->getDeclName() << EC2->getInitVal().toString(10); Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1251,7 +1251,9 @@ case Decl::EnumConstant: // C++ [basic.link]p4: an enumerator has the linkage of its enumeration. - return getLVForDecl(cast(D->getDeclContext()), computation); + if (D->getASTContext().getLangOpts().CPlusPlus) + return getLVForDecl(cast(D->getDeclContext()), computation); + return LinkageInfo::visible_none(); case Decl::Typedef: case Decl::TypeAlias: Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -4319,8 +4319,15 @@ return; } - if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) - ParseEnumBody(StartLoc, TagDecl); + if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) { + Decl *D = SkipBody.CheckSameAsPrevious ? SkipBody.New : TagDecl; + ParseEnumBody(StartLoc, D); + if (SkipBody.CheckSameAsPrevious && + !Actions.ActOnDuplicateDefinition(DS, TagDecl, SkipBody)) { + DS.SetTypeSpecError(); + return; + } + } if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, NameLoc.isValid() ? NameLoc : StartLoc, @@ -4392,11 +4399,9 @@ } // Install the enumerator constant into EnumDecl. - Decl *EnumConstDecl = Actions.ActOnEnumConstant(getCurScope(), EnumDecl, - LastEnumConstDecl, - IdentLoc, Ident, - attrs.getList(), EqualLoc, - AssignedVal.get()); + Decl *EnumConstDecl = Actions.ActOnEnumConstant( + getCurScope(), EnumDecl, LastEnumConstDecl, IdentLoc, Ident, + attrs.getList(), EqualLoc, AssignedVal.get()); EnumAvailabilityDiags.back().done(); EnumConstantDecls.push_back(EnumConstDecl); Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -1910,8 +1910,18 @@ else if (getLangOpts().CPlusPlus) ParseCXXMemberSpecification(StartLoc, AttrFixitLoc, attrs, TagType, TagOrTempResult.get()); - else - ParseStructUnionBody(StartLoc, TagType, TagOrTempResult.get()); + else { + Decl *D = + SkipBody.CheckSameAsPrevious ? SkipBody.New : TagOrTempResult.get(); + // Parse the definition body. + ParseStructUnionBody(StartLoc, TagType, D); + if (SkipBody.CheckSameAsPrevious && + !Actions.ActOnDuplicateDefinition(DS, TagOrTempResult.get(), + SkipBody)) { + DS.SetTypeSpecError(); + return; + } + } } if (!TagOrTempResult.isInvalid()) Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -13233,6 +13233,55 @@ if (TUK == TUK_Friend || TUK == TUK_Reference) Redecl = NotForRedeclaration; + /// Create a new tag decl in C/ObjC. Since the ODR-like semantics for ObjC/C + /// implemented asks for structural equivalence checking, the returned decl + /// here is passed back to the parser, allowing the tag body to be parsed. + auto createTagFromNewDecl = [&]() -> TagDecl * { + assert(!getLangOpts().CPlusPlus && "not meant for C++ usage"); + // If there is an identifier, use the location of the identifier as the + // location of the decl, otherwise use the location of the struct/union + // keyword. + SourceLocation Loc = NameLoc.isValid() ? NameLoc : KWLoc; + TagDecl *New = nullptr; + + if (Kind == TTK_Enum) { + New = EnumDecl::Create(Context, SearchDC, KWLoc, Loc, Name, nullptr, + ScopedEnum, ScopedEnumUsesClassTag, + !EnumUnderlying.isNull()); + // If this is an undefined enum, bail. + if (TUK != TUK_Definition && !Invalid) + return nullptr; + if (EnumUnderlying) { + EnumDecl *ED = cast(New); + if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast()) + ED->setIntegerTypeSourceInfo(TI); + else + ED->setIntegerType(QualType(EnumUnderlying.get(), 0)); + ED->setPromotionType(ED->getIntegerType()); + } + } else { // struct/union + New = RecordDecl::Create(Context, Kind, SearchDC, KWLoc, Loc, Name, + nullptr); + } + + if (RecordDecl *RD = dyn_cast(New)) { + // Add alignment attributes if necessary; these attributes are checked + // when the ASTContext lays out the structure. + // + // It is important for implementing the correct semantics that this + // happen here (in ActOnTag). The #pragma pack stack is + // maintained as a result of parser callbacks which can occur at + // many points during the parsing of a struct declaration (because + // the #pragma tokens are effectively skipped over during the + // parsing of the struct). + if (TUK == TUK_Definition) { + AddAlignmentAttributesForRecord(RD); + AddMsStructLayoutForRecord(RD); + } + } + return New; + }; + LookupResult Previous(*this, Name, NameLoc, LookupTagName, Redecl); if (Name && SS.isNotEmpty()) { // We have a nested-name tag ('struct foo::bar'). @@ -13638,16 +13687,29 @@ TSK_ExplicitSpecialization; } + // Allow ODR for ObjC/C in the sense that we won't keep more that + // one definition around (merge them). However, ensure the decl + // passes the structural compatibility check in C11 6.2.7/1. + bool AllowODR = getLangOpts().CPlusPlus || getLangOpts().ObjC1 || + getLangOpts().C11; NamedDecl *Hidden = nullptr; - if (SkipBody && getLangOpts().CPlusPlus && - !hasVisibleDefinition(Def, &Hidden)) { + if (SkipBody && AllowODR && !hasVisibleDefinition(Def, &Hidden)) { // There is a definition of this tag, but it is not visible. We // explicitly make use of C++'s one definition rule here, and // assume that this definition is identical to the hidden one // we already have. Make the existing definition visible and // use it in place of this one. - SkipBody->ShouldSkip = true; - makeMergedDefinitionVisible(Hidden); + if (!getLangOpts().CPlusPlus) { + // Postpone making the old definition visible until after we + // complete parsing the new one and do the structural + // comparison. + SkipBody->CheckSameAsPrevious = true; + SkipBody->New = createTagFromNewDecl(); + SkipBody->Previous = Hidden; + } else { + SkipBody->ShouldSkip = true; + makeMergedDefinitionVisible(Hidden); + } return Def; } else if (!IsExplicitSpecializationAfterInstantiation) { // A redeclaration in function prototype scope in C isn't @@ -13875,7 +13937,7 @@ // the ASTContext lays out the structure. // // It is important for implementing the correct semantics that this - // happen here (in act on tag decl). The #pragma pack stack is + // happen here (in ActOnTag). The #pragma pack stack is // maintained as a result of parser callbacks which can occur at // many points during the parsing of a struct declaration (because // the #pragma tokens are effectively skipped over during the @@ -14011,6 +14073,16 @@ AddPushedVisibilityAttribute(Tag); } +bool Sema::ActOnDuplicateDefinition(DeclSpec &DS, Decl *Prev, + SkipBodyInfo &SkipBody) { + if (!hasStructuralCompatLayout(Prev, SkipBody.New)) + return false; + + // Make the previous decl visible. + makeMergedDefinitionVisible(SkipBody.Previous); + return true; +} + Decl *Sema::ActOnObjCContainerStartDefinition(Decl *IDecl) { assert(isa(IDecl) && "ActOnObjCContainerStartDefinition - Not ObjCContainerDecl"); @@ -15432,7 +15504,7 @@ // different from T: // - every enumerator of every member of class T that is an unscoped // enumerated type - if (!TheEnumDecl->isScoped()) + if (getLangOpts().CPlusPlus && !TheEnumDecl->isScoped()) DiagnoseClassNameShadow(TheEnumDecl->getDeclContext(), DeclarationNameInfo(Id, IdLoc)); Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -15,6 +15,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/ASTStructuralEquivalence.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -7111,6 +7112,20 @@ return false; } +bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) { + llvm::DenseSet> NonEquivalentDecls; + if (!Suggested) + return false; + + // FIXME: Add a specific mode for C11 6.2.7/1 in StructuralEquivalenceContext + // and isolate from other C++ specific checks. + StructuralEquivalenceContext Ctx( + D->getASTContext(), Suggested->getASTContext(), NonEquivalentDecls, + false /*StrictTypeSpelling*/, true /*Complain*/, + true /*ErrorOnTagTypeMismatch*/); + return Ctx.IsStructurallyEquivalent(D, Suggested); +} + /// \brief Determine whether there is any declaration of \p D that was ever a /// definition (perhaps before module merging) and is currently visible. /// \param D The definition of the entity. Index: test/Index/linkage.c =================================================================== --- test/Index/linkage.c +++ test/Index/linkage.c @@ -20,7 +20,7 @@ // CHECK: EnumDecl=Baz:3:6 (Definition)linkage=External -// CHECK: EnumConstantDecl=Qux:3:12 (Definition)linkage=External +// CHECK: EnumConstantDecl=Qux:3:12 (Definition)linkage=NoLinkage // CHECK: VarDecl=x:4:5linkage=External // CHECK: FunctionDecl=foo:5:6linkage=External // CHECK: VarDecl=w:6:12linkage=Internal Index: test/Modules/Inputs/F.framework/Headers/F.h =================================================================== --- /dev/null +++ test/Modules/Inputs/F.framework/Headers/F.h @@ -0,0 +1 @@ +// F.h Index: test/Modules/Inputs/F.framework/Modules/module.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/F.framework/Modules/module.modulemap @@ -0,0 +1,7 @@ +framework module F [extern_c] [system] { + umbrella header "F.h" + module * { + export * + } + export * +} Index: test/Modules/Inputs/F.framework/Modules/module.private.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/F.framework/Modules/module.private.modulemap @@ -0,0 +1,7 @@ +module F.Private [system] { + explicit module NS { + header "NS.h" + export * + } + export * +} Index: test/Modules/Inputs/F.framework/PrivateHeaders/NS.h =================================================================== --- /dev/null +++ test/Modules/Inputs/F.framework/PrivateHeaders/NS.h @@ -0,0 +1,19 @@ +struct NS { + int a; + int b; +}; + +enum NSE { + FST = 22, + SND = 43, + TRD = 55 +}; + +#define NS_ENUM(_type, _name) \ + enum _name : _type _name; \ + enum _name : _type + +typedef NS_ENUM(int, NSMyEnum) { + MinX = 11, + MinXOther = MinX, +}; Index: test/Modules/elaborated-type-specifier-from-hidden-module.m =================================================================== --- test/Modules/elaborated-type-specifier-from-hidden-module.m +++ test/Modules/elaborated-type-specifier-from-hidden-module.m @@ -4,12 +4,11 @@ @import ElaboratedTypeStructs.Empty; // The structs are now hidden. struct S1 *x; struct S2 *y; -// FIXME: compatible definition should not be an error. -struct S2 { int x; }; // expected-error {{redefinition}} +struct S2 { int x; }; struct S3 *z; // Incompatible definition. -struct S3 { float y; }; // expected-error {{redefinition}} -// expected-note@elaborated-type-structs.h:* 2 {{previous definition is here}} +struct S3 { float y; }; // expected-error {{has incompatible definitions}} // expected-note {{field has name}} +// expected-note@Inputs/elaborated-type-structs.h:3 {{field has name}} @import ElaboratedTypeStructs.Structs; Index: test/Modules/redefinition-c-tagtypes.m =================================================================== --- /dev/null +++ test/Modules/redefinition-c-tagtypes.m @@ -0,0 +1,48 @@ +// RUN: rm -rf %t.cache +// RUN: %clang_cc1 -fsyntax-only %s -fmodules -fmodules-cache-path=%t.cache \ +// RUN: -fimplicit-module-maps -F%S/Inputs -verify +// RUN: %clang_cc1 -fsyntax-only %s -fmodules -fmodules-cache-path=%t.cache \ +// RUN: -fimplicit-module-maps -F%S/Inputs -DCHANGE_TAGS -verify +#include "F/F.h" + +#ifndef CHANGE_TAGS +// expected-no-diagnostics +#endif + +struct NS { + int a; +#ifndef CHANGE_TAGS + int b; +#else + int c; // expected-note {{field has name 'c' here}} + // expected-error@redefinition-c-tagtypes.m:12 {{type 'struct NS' has incompatible definitions}} + // expected-note@Inputs/F.framework/PrivateHeaders/NS.h:3 {{field has name 'b' here}} +#endif +}; + +enum NSE { + FST = 22, +#ifndef CHANGE_TAGS + SND = 43, +#else + SND = 44, // expected-note {{enumerator 'SND' with value 44 here}} + // expected-error@redefinition-c-tagtypes.m:23 {{type 'enum NSE' has incompatible definitions}} + // expected-note@Inputs/F.framework/PrivateHeaders/NS.h:8 {{enumerator 'SND' with value 43 here}} +#endif + TRD = 55 +}; + +#define NS_ENUM(_type, _name) \ + enum _name : _type _name; \ + enum _name : _type + +typedef NS_ENUM(int, NSMyEnum) { + MinX = 11, +#ifndef CHANGE_TAGS + MinXOther = MinX, +#else + MinXOther = TRD, // expected-note {{enumerator 'MinXOther' with value 55 here}} + // expected-error@redefinition-c-tagtypes.m:39 {{type 'enum NSMyEnum' has incompatible definitions}} + // expected-note@Inputs/F.framework/PrivateHeaders/NS.h:18 {{enumerator 'MinXOther' with value 11 here}} +#endif +}; Index: test/Modules/redefinition-same-header.m =================================================================== --- test/Modules/redefinition-same-header.m +++ test/Modules/redefinition-same-header.m @@ -6,15 +6,7 @@ // expected-note-re@Inputs/SameHeader/B.h:3 {{'{{.*}}C.h' included multiple times, additional include site in header from module 'X.B'}} // expected-note@Inputs/SameHeader/module.modulemap:6 {{X.B defined here}} // expected-note-re@redefinition-same-header.m:20 {{'{{.*}}C.h' included multiple times, additional include site here}} +// expected-warning@Inputs/SameHeader/C.h:9 {{typedef requires a name}} -// expected-error@Inputs/SameHeader/C.h:5 {{redefinition of 'aaa'}} -// expected-note-re@Inputs/SameHeader/B.h:3 {{'{{.*}}C.h' included multiple times, additional include site in header from module 'X.B'}} -// expected-note@Inputs/SameHeader/module.modulemap:6 {{X.B defined here}} -// expected-note-re@redefinition-same-header.m:20 {{'{{.*}}C.h' included multiple times, additional include site here}} - -// expected-error@Inputs/SameHeader/C.h:9 {{redefinition of 'fd_set'}} -// expected-note-re@Inputs/SameHeader/B.h:3 {{'{{.*}}C.h' included multiple times, additional include site in header from module 'X.B'}} -// expected-note@Inputs/SameHeader/module.modulemap:6 {{X.B defined here}} -// expected-note-re@redefinition-same-header.m:20 {{'{{.*}}C.h' included multiple times, additional include site here}} #include "A.h" // maps to a modular #include "C.h" // textual include