diff --git a/clang/include/clang/AST/ASTStructuralEquivalence.h b/clang/include/clang/AST/ASTStructuralEquivalence.h --- a/clang/include/clang/AST/ASTStructuralEquivalence.h +++ b/clang/include/clang/AST/ASTStructuralEquivalence.h @@ -69,15 +69,20 @@ /// \c true if the last diagnostic came from ToCtx. bool LastDiagFromC2 = false; + /// \c true if canonical types should be used for each Decl before + /// comparing for structural equivalnce + bool UseCanonicalDecls; + StructuralEquivalenceContext( ASTContext &FromCtx, ASTContext &ToCtx, llvm::DenseSet> &NonEquivalentDecls, - StructuralEquivalenceKind EqKind, - bool StrictTypeSpelling = false, bool Complain = true, - bool ErrorOnTagTypeMismatch = false) + StructuralEquivalenceKind EqKind, bool StrictTypeSpelling = false, + bool Complain = true, bool ErrorOnTagTypeMismatch = false, + bool UseCanonicalDecls = true) : FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls), EqKind(EqKind), StrictTypeSpelling(StrictTypeSpelling), - ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain) {} + ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain), + UseCanonicalDecls(UseCanonicalDecls) {} DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID); DiagnosticBuilder Diag2(SourceLocation Loc, unsigned DiagID); diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1084,6 +1084,10 @@ /// been completed. std::deque PendingDeclContextInfos; + /// C/ObjC definitions in which the structural equivalence check fails + llvm::SmallDenseMap, 2> + PendingStructuralMismatches; + /// The set of NamedDecls that have been loaded, but are members of a /// context that has been merged into another context where the corresponding /// declaration is either missing or has not yet been loaded. @@ -1412,6 +1416,7 @@ void finishPendingActions(); void diagnoseOdrViolations(); + void diagnoseStructuralMismatches(); void pushExternalDeclIntoScope(NamedDecl *D, DeclarationName Name); diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1574,8 +1574,10 @@ Decl *D1, Decl *D2) { // FIXME: Check for known structural equivalences via a callback of some sort. - D1 = D1->getCanonicalDecl(); - D2 = D2->getCanonicalDecl(); + if (Context.UseCanonicalDecls) { + D1 = D1->getCanonicalDecl(); + D2 = D2->getCanonicalDecl(); + } std::pair P{D1, D2}; // Check whether we already know that these two declarations are not diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -10,14 +10,14 @@ // //===----------------------------------------------------------------------===// -#include "clang/Serialization/ASTRecordReader.h" #include "ASTCommon.h" #include "ASTReaderInternals.h" -#include "clang/AST/AbstractTypeReader.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/ASTStructuralEquivalence.h" #include "clang/AST/ASTUnresolvedSet.h" +#include "clang/AST/AbstractTypeReader.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -30,8 +30,8 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExternalASTSource.h" #include "clang/AST/NestedNameSpecifier.h" -#include "clang/AST/OpenMPClause.h" #include "clang/AST/ODRHash.h" +#include "clang/AST/OpenMPClause.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" @@ -75,6 +75,7 @@ #include "clang/Sema/Weak.h" #include "clang/Serialization/ASTBitCodes.h" #include "clang/Serialization/ASTDeserializationListener.h" +#include "clang/Serialization/ASTRecordReader.h" #include "clang/Serialization/ContinuousRangeMap.h" #include "clang/Serialization/GlobalModuleIndex.h" #include "clang/Serialization/InMemoryModuleCache.h" @@ -9263,6 +9264,30 @@ PendingMergedDefinitionsToDeduplicate.clear(); } +void ASTReader::diagnoseStructuralMismatches() { + if (PendingStructuralMismatches.empty()) + return; + + // Updates while doing structural equivalence checks may in turn find and + // diagnose other failures, so take ownership of it first. + auto StructuralMismatches = std::move(PendingStructuralMismatches); + PendingStructuralMismatches.clear(); + + for (auto &Mismatches : StructuralMismatches) { + auto *Canon = Mismatches.first; + for (auto *D : Mismatches.second) { + llvm::DenseSet> NonEquivalentDecls; + + StructuralEquivalenceContext Ctx( + D->getASTContext(), Canon->getASTContext(), NonEquivalentDecls, + StructuralEquivalenceKind::Default, false /*StrictTypeSpelling*/, + true /*Complain*/, true /*ErrorOnTagTypeMismatch*/, + false /*UseCanonicalDecls*/); + (void)Ctx.IsEquivalent(D, Canon); + } + } +} + void ASTReader::diagnoseOdrViolations() { if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && PendingFunctionOdrMergeFailures.empty() && @@ -11381,6 +11406,7 @@ ReadTimer->stopTimer(); diagnoseOdrViolations(); + diagnoseStructuralMismatches(); // We are not in recursive loading, so it's safe to pass the "interesting" // decls to the consumer. diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -14,6 +14,7 @@ #include "ASTCommon.h" #include "ASTReaderInternals.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTStructuralEquivalence.h" #include "clang/AST/Attr.h" #include "clang/AST/AttrIterator.h" #include "clang/AST/Decl.h" @@ -723,8 +724,22 @@ llvm_unreachable("unexpected tag info kind"); } - if (!isa(TD)) - mergeRedeclarable(TD, Redecl); + if (isa(TD)) + return Redecl; + + mergeRedeclarable(TD, Redecl); + // Handle merging in C/Objective-C + if (!Reader.getContext().getLangOpts().CPlusPlus) { + auto *Canon = TD->getCanonicalDecl(); + if (TD == Canon) + return Redecl; + + // Only check decls loaded from different external sources and + // delay the actual checking using PendingStructuralMismatches. + if (TD->getGlobalID() != Canon->getGlobalID()) + Reader.PendingStructuralMismatches[Canon].push_back(TD); + } + return Redecl; } diff --git a/clang/test/Modules/Inputs/merge-in-c/tag-types/module.modulemap b/clang/test/Modules/Inputs/merge-in-c/tag-types/module.modulemap new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-in-c/tag-types/module.modulemap @@ -0,0 +1,10 @@ + +module X { + header "x.h" + export * +} + +module Y { + header "y.h" + export * +} diff --git a/clang/test/Modules/Inputs/merge-in-c/tag-types/x.h b/clang/test/Modules/Inputs/merge-in-c/tag-types/x.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-in-c/tag-types/x.h @@ -0,0 +1,4 @@ + +struct Z { + int m; +}; diff --git a/clang/test/Modules/Inputs/merge-in-c/tag-types/y.h b/clang/test/Modules/Inputs/merge-in-c/tag-types/y.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-in-c/tag-types/y.h @@ -0,0 +1,4 @@ + +struct Z { + double m; +}; diff --git a/clang/test/Modules/merge-tag-types.c b/clang/test/Modules/merge-tag-types.c new file mode 100644 --- /dev/null +++ b/clang/test/Modules/merge-tag-types.c @@ -0,0 +1,14 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x objective-c -I%S/Inputs/merge-in-c/tag-types -verify %s + +@import Y; +@import X; + +void foo() { + struct Z z; + z.m = 2.0; +} + +// expected-error@y.h:2 {{type 'struct Z' has incompatible definitions in different translation units}} +// expected-note@y.h:3 {{field 'm' has type 'double' here}} +// expected-note@x.h:3 {{field 'm' has type 'int' here}}