diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 5c40e4118fcbd..81c4839089825 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -872,6 +872,27 @@ ERROR(need_hermetic_seal_to_import_module,none, "current compilation does not have -experimental-hermetic-seal-at-link", (Identifier)) +ERROR(modularization_issue_decl_moved,Fatal, + "reference to %select{top-level|type}0 %1 broken by a context change; " + "%1 was expected to be in %2, but now a candidate is found only in %3", + (bool, DeclName, Identifier, Identifier)) +ERROR(modularization_issue_decl_type_changed,Fatal, + "reference to %select{top-level|type}0 %1 broken by a context change; " + "the details of %1 %select{from %2|}5 changed since building '%3'" + "%select{|, it was in %2 and is now found in %4}5", + (bool, DeclName, Identifier, StringRef, Identifier, bool)) +ERROR(modularization_issue_decl_not_found,Fatal, + "reference to %select{top-level|type}0 %1 broken by a context change; " + "%1 is not found, it was expected to be in %2", + (bool, DeclName, Identifier)) + +NOTE(modularization_issue_side_effect_extension_error,none, + "could not deserialize extension", + ()) +NOTE(modularization_issue_side_effect_type_error,none, + "could not deserialize type for %0", + (DeclName)) + ERROR(reserved_member_name,none, "type member must not be named %0, since it would conflict with the" " 'foo.%1' expression", (DeclName, StringRef)) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 636ee240ea282..e14225ae81cff 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -157,6 +157,8 @@ const char InvalidRecordKindError::ID = '\0'; void InvalidRecordKindError::anchor() {} const char UnsafeDeserializationError::ID = '\0'; void UnsafeDeserializationError::anchor() {} +const char ModularizationError::ID = '\0'; +void ModularizationError::anchor() {} static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error); @@ -179,18 +181,108 @@ void ModuleFile::fatal(llvm::Error error) const { Core->fatal(diagnoseFatal(std::move(error))); } +SourceLoc ModuleFile::getSourceLoc() const { + auto &SourceMgr = getContext().Diags.SourceMgr; + auto filename = getModuleFilename(); + auto bufferID = SourceMgr.getIDForBufferIdentifier(filename); + if (!bufferID) + bufferID = SourceMgr.addMemBufferCopy(StringRef(), filename); + return SourceMgr.getLocForBufferStart(*bufferID); +} + +void +ModularizationError::diagnose(const ModuleFile *MF, + DiagnosticBehavior limit) const { + auto &ctx = MF->getContext(); + + auto diagnoseError = [&](Kind errorKind) { + switch (errorKind) { + case Kind::DeclMoved: + return ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_moved, + declIsType, name, expectedModuleName, + foundModuleName); + case Kind::DeclKindChanged: + return + ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_type_changed, + declIsType, name, expectedModuleName, + referencedFromModuleName, foundModuleName, + foundModuleName != expectedModuleName); + case Kind::DeclNotFound: + return ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_not_found, + declIsType, name, expectedModuleName); + } + llvm_unreachable("Unhandled ModularizationError::Kind in switch."); + }; + + auto inFlight = diagnoseError(errorKind); + inFlight.limitBehavior(limit); + inFlight.flush(); + + // We could pass along the `path` information through notes. + // However, for a top-level decl a path would just duplicate the + // expected module name and the decl name from the diagnostic. +} + +void TypeError::diagnose(const ModuleFile *MF) const { + MF->getContext().Diags.diagnose(MF->getSourceLoc(), + diag::modularization_issue_side_effect_type_error, + name); +} + +void ExtensionError::diagnose(const ModuleFile *MF) const { + MF->getContext().Diags.diagnose(MF->getSourceLoc(), + diag::modularization_issue_side_effect_extension_error); +} + llvm::Error ModuleFile::diagnoseFatal(llvm::Error error) const { - if (FileContext) - getContext().Diags.diagnose(SourceLoc(), diag::serialization_fatal, - Core->Name); + + auto &ctx = getContext(); + if (FileContext) { + if (ctx.LangOpts.EnableDeserializationRecovery) { + // Attempt to report relevant errors as diagnostics. + // At this time, only ModularizationErrors are reported directly. They + // can get here either directly or as underlying causes to a TypeError or + // and ExtensionError. + auto handleModularizationError = + [&](const ModularizationError &modularError) -> llvm::Error { + modularError.diagnose(this); + return llvm::Error::success(); + }; + error = llvm::handleErrors(std::move(error), + handleModularizationError, + [&](TypeError &typeError) -> llvm::Error { + if (typeError.diagnoseUnderlyingReason(handleModularizationError)) { + typeError.diagnose(this); + return llvm::Error::success(); + } + return llvm::make_error(std::move(typeError)); + }, + [&](ExtensionError &extError) -> llvm::Error { + if (extError.diagnoseUnderlyingReason(handleModularizationError)) { + extError.diagnose(this); + return llvm::Error::success(); + } + return llvm::make_error(std::move(extError)); + }); + + // If no error is left, it was reported as a diagnostic. There's no + // need to crash. + if (!error) + return llvm::Error::success(); + } + + // General deserialization failure message. + ctx.Diags.diagnose(getSourceLoc(), diag::serialization_fatal, Core->Name); + } // Unless in the debugger, crash. ModuleFileSharedCore::fatal() calls abort(). // This allows aggregation of crash logs for compiler development, but in a // long-running process like LLDB this is undesirable. Only abort() if not in // the debugger. - if (!getContext().LangOpts.DebuggerSupport) + if (!ctx.LangOpts.DebuggerSupport) Core->fatal(std::move(error)); - // Otherwise, augment the error with contextual information and pass it back. + // Otherwise, augment the error with contextual information at this point + // of failure and pass it back to be reported later. std::string msg; { llvm::raw_string_ostream os(msg); @@ -1780,18 +1872,21 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // is mostly for compiler engineers to understand a likely solution at a // quick glance. SmallVector strScratch; - SmallVector notes; - auto declName = getXRefDeclNameForError(); + + auto errorKind = ModularizationError::Kind::DeclNotFound; + Identifier foundIn; + bool isType = false; + if (recordID == XREF_TYPE_PATH_PIECE || recordID == XREF_VALUE_PATH_PIECE) { auto &ctx = getContext(); for (auto nameAndModule : ctx.getLoadedModules()) { - auto baseModule = nameAndModule.second; + auto otherModule = nameAndModule.second; IdentifierID IID; IdentifierID privateDiscriminator = 0; TypeID TID = 0; - bool isType = (recordID == XREF_TYPE_PATH_PIECE); + isType = (recordID == XREF_TYPE_PATH_PIECE); bool inProtocolExt = false; bool importedFromClang = false; bool isStatic = false; @@ -1815,10 +1910,10 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { values.clear(); if (privateDiscriminator) { - baseModule->lookupMember(values, baseModule, name, + otherModule->lookupMember(values, otherModule, name, getIdentifier(privateDiscriminator)); } else { - baseModule->lookupQualified(baseModule, DeclNameRef(name), + otherModule->lookupQualified(otherModule, DeclNameRef(name), NL_QualifiedDefault, values); } @@ -1832,30 +1927,31 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // Found a full match in a different module. It should be a different // one because otherwise it would have succeeded on the first search. // This is usually caused by the use of poorly modularized headers. - auto line = "There is a matching '" + - declName.getString(strScratch).str() + - "' in module '" + - std::string(nameAndModule.first.str()) + - "'. If this is imported from clang, please make sure " + - "the header is part of a single clang module."; - notes.emplace_back(line); + errorKind = ModularizationError::Kind::DeclMoved; + foundIn = otherModule->getName(); + break; } else if (hadAMatchBeforeFiltering) { // Found a match that was filtered out. This may be from the same // expected module if there's a type difference. This can be caused // by the use of different Swift language versions between a library // with serialized SIL and a client. - auto line = "'" + - declName.getString(strScratch).str() + - "' in module '" + - std::string(nameAndModule.first.str()) + - "' was filtered out."; - notes.emplace_back(line); + errorKind = ModularizationError::Kind::DeclKindChanged; + foundIn = otherModule->getName(); + break; } } } - return llvm::make_error("top-level value not found", pathTrace, - declName, notes); + auto declName = getXRefDeclNameForError(); + auto expectedIn = baseModule->getName(); + auto referencedFrom = getName(); + return llvm::make_error(declName, + isType, + errorKind, + expectedIn, + referencedFrom, + foundIn, + pathTrace); } // Filters for values discovered in the remaining path pieces. @@ -7256,7 +7352,8 @@ static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error) { // implementation-only import hiding types and decls. // rdar://problem/60291019 if (error.isA() || - error.isA()) { + error.isA() || + error.isA()) { consumeError(std::move(error)); return llvm::Error::success(); } @@ -7269,7 +7366,8 @@ static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error) { auto *TE = static_cast(errorInfo.get()); if (TE->underlyingReasonIsA() || - TE->underlyingReasonIsA()) { + TE->underlyingReasonIsA() || + TE->underlyingReasonIsA()) { consumeError(std::move(errorInfo)); return llvm::Error::success(); } diff --git a/lib/Serialization/DeserializationErrors.h b/lib/Serialization/DeserializationErrors.h index 4e54f9d316349..b06c8358ecebe 100644 --- a/lib/Serialization/DeserializationErrors.h +++ b/lib/Serialization/DeserializationErrors.h @@ -24,6 +24,17 @@ namespace swift { namespace serialization { +[[nodiscard]] +static inline std::unique_ptr +takeErrorInfo(llvm::Error error) { + std::unique_ptr result; + llvm::handleAllErrors(std::move(error), + [&](std::unique_ptr info) { + result = std::move(info); + }); + return result; +} + /// This error is generated by ModuleFile::diagnoseFatal(). All /// FatalDeserializationError has already been added to the DiagnosticsEngine /// upon creation. @@ -289,25 +300,16 @@ class XRefError : public llvm::ErrorInfo { XRefTracePath path; const char *message; - SmallVector notes; public: template - XRefError(const char (&message)[N], XRefTracePath path, DeclName name, - SmallVector notes = {}) - : path(path), message(message), notes(notes) { + XRefError(const char (&message)[N], XRefTracePath path, DeclName name) + : path(path), message(message) { this->name = name; } void log(raw_ostream &OS) const override { OS << message << " (" << name << ")\n"; path.print(OS); - - if (!notes.empty()) { - OS << "Notes:\n"; - for (auto &line : notes) { - OS << "* " << line << "\n"; - } - } } std::error_code convertToErrorCode() const override { @@ -335,6 +337,75 @@ class XRefNonLoadedModuleError : } }; +/// Project issue affecting modules. Usually a change in the context between +/// the time a module was built and when it was imported. +class ModularizationError : public llvm::ErrorInfo { + friend ErrorInfo; + static const char ID; + void anchor() override; + +public: + enum class Kind { + DeclMoved, + DeclKindChanged, + DeclNotFound + }; + +private: + DeclName name; + bool declIsType; + Kind errorKind; + Identifier expectedModuleName; + StringRef referencedFromModuleName; + Identifier foundModuleName; + XRefTracePath path; + +public: + explicit ModularizationError(DeclName name, bool declIsType, Kind errorKind, + Identifier expectedModuleName, + StringRef referencedFromModuleName, + Identifier foundModuleName, + XRefTracePath path): + name(name), declIsType(declIsType), errorKind(errorKind), + expectedModuleName(expectedModuleName), + referencedFromModuleName(referencedFromModuleName), + foundModuleName(foundModuleName), path(path) {} + + void diagnose(const ModuleFile *MF, + DiagnosticBehavior limit = DiagnosticBehavior::Fatal) const; + + void log(raw_ostream &OS) const override { + OS << "modularization issue on '" << name << "', reference from '"; + OS << referencedFromModuleName << "' not resolvable: "; + switch (errorKind) { + case Kind::DeclMoved: + OS << "expected in '" << expectedModuleName << "' but found in '"; + OS << foundModuleName << "'"; + break; + case Kind::DeclKindChanged: + OS << "decl details changed between what was imported from '"; + OS << expectedModuleName << "' and what is now imported from '"; + OS << foundModuleName << "'"; + break; + case Kind::DeclNotFound: + OS << "not found, expected in '" << expectedModuleName << "'"; + break; + } + OS << "\n"; + path.print(OS); + } + + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + + bool isA(const void *const ClassID) const override { + return ClassID == classID() || ErrorInfoBase::isA(ClassID); + } + + static const void *classID() { return &ID; } +}; + class OverrideError : public llvm::ErrorInfo { private: @@ -359,34 +430,68 @@ class OverrideError : public llvm::ErrorInfo { +// Service class for errors with an underlying cause. +class ErrorWithUnderlyingReason { + std::unique_ptr underlyingReason; + +public: + explicit ErrorWithUnderlyingReason (std::unique_ptr reason) : + underlyingReason(std::move(reason)) {} + + template + bool underlyingReasonIsA() const { + if (!underlyingReason) + return false; + return underlyingReason->isA(); + } + + void log(raw_ostream &OS) const { + if (underlyingReason) { + OS << "\nCaused by: "; + underlyingReason->log(OS); + } + } + + // Returns \c true if the error was diagnosed. + template + bool diagnoseUnderlyingReason(HandlerT &&handler) { + if (underlyingReason && + llvm::ErrorHandlerTraits::appliesTo(*underlyingReason)) { + auto error = llvm::ErrorHandlerTraits::apply( + std::forward(handler), + std::move(underlyingReason)); + if (!error) { + // The underlying reason was diagnosed. + return true; + } else { + underlyingReason = takeErrorInfo(std::move(error)); + return false; + } + } + return false; + } +}; + +class TypeError : public llvm::ErrorInfo, + public ErrorWithUnderlyingReason { friend ErrorInfo; static const char ID; void anchor() override; - std::unique_ptr underlyingReason; public: explicit TypeError(DeclName name, std::unique_ptr reason, Flags flags={}, unsigned numVTableEntries=0) - : underlyingReason(std::move(reason)) { + : ErrorWithUnderlyingReason(std::move(reason)) { this->name = name; this->flags = flags; this->numVTableEntries = numVTableEntries; } - template - bool underlyingReasonIsA() const { - if (!underlyingReason) - return false; - return underlyingReason->isA(); - } + void diagnose(const ModuleFile *MF) const; void log(raw_ostream &OS) const override { OS << "Could not deserialize type for '" << name << "'"; - if (underlyingReason) { - OS << "\nCaused by: "; - underlyingReason->log(OS); - } + ErrorWithUnderlyingReason::log(OS); } std::error_code convertToErrorCode() const override { @@ -394,23 +499,21 @@ class TypeError : public llvm::ErrorInfo { } }; -class ExtensionError : public llvm::ErrorInfo { +class ExtensionError : public llvm::ErrorInfo, + public ErrorWithUnderlyingReason { friend ErrorInfo; static const char ID; void anchor() override; - std::unique_ptr underlyingReason; - public: explicit ExtensionError(std::unique_ptr reason) - : underlyingReason(std::move(reason)) {} + : ErrorWithUnderlyingReason(std::move(reason)) {} + + void diagnose(const ModuleFile *MF) const; void log(raw_ostream &OS) const override { OS << "could not deserialize extension"; - if (underlyingReason) { - OS << ": "; - underlyingReason->log(OS); - } + ErrorWithUnderlyingReason::log(OS); } std::error_code convertToErrorCode() const override { @@ -418,23 +521,20 @@ class ExtensionError : public llvm::ErrorInfo { } }; -class SILEntityError : public llvm::ErrorInfo { +class SILEntityError : public llvm::ErrorInfo, + public ErrorWithUnderlyingReason { friend ErrorInfo; static const char ID; void anchor() override; - std::unique_ptr underlyingReason; StringRef name; public: SILEntityError(StringRef name, std::unique_ptr reason) - : underlyingReason(std::move(reason)), name(name) {} + : ErrorWithUnderlyingReason(std::move(reason)), name(name) {} void log(raw_ostream &OS) const override { OS << "could not deserialize SIL entity '" << name << "'"; - if (underlyingReason) { - OS << ": "; - underlyingReason->log(OS); - } + ErrorWithUnderlyingReason::log(OS); } std::error_code convertToErrorCode() const override { @@ -515,17 +615,6 @@ class UnsafeDeserializationError : public llvm::ErrorInfo -takeErrorInfo(llvm::Error error) { - std::unique_ptr result; - llvm::handleAllErrors(std::move(error), - [&](std::unique_ptr info) { - result = std::move(info); - }); - return result; -} - class PrettyStackTraceModuleFile : public llvm::PrettyStackTraceEntry { const char *Action; const ModuleFile &MF; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 5fff4c599a7b8..3b1e9fb20bb51 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -3946,7 +3946,7 @@ void SILDeserializer::getAllWitnessTables() { // import, it is safe to ignore for this function's purpose. consumeError(maybeTable.takeError()); } else { - MF->fatal(maybeTable.takeError()); + MF->diagnoseAndConsumeFatal(maybeTable.takeError()); } } } diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 29c9ad27431c3..aabc132c150d2 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -352,7 +352,11 @@ class ModuleFile } /// Enrich \c error with contextual information, emits a fatal diagnostic in - /// the ASTContext's DignosticsEngine, and return the augmented error. + /// the ASTContext's DiagnosticsEngine, and return the augmented error. + /// + /// The `diagnoseFatal` methods return only in LLDB where high error + /// tolerance is expected, or when hitting a project error. During normal + /// compilation, most calls won't return and lead to a compiler crash. llvm::Error diagnoseFatal(llvm::Error error) const; /// Emit a generic deserialization error via \c diagnoseFatal(). @@ -667,6 +671,9 @@ class ModuleFile getTransitiveLoadingBehavior(const Dependency &dependency, bool forTestable) const; + /// Generate a \c SourceLoc pointing at the loaded swiftmodule file. + SourceLoc getSourceLoc() const; + /// Returns `true` if there is a buffer that might contain source code where /// other parts of the compiler could have emitted diagnostics, to indicate /// that the object must be kept alive as long as the diagnostics exist. diff --git a/test/Serialization/Recovery/crash-xref.swift b/test/Serialization/Recovery/crash-xref.swift index df50edd22d21f..fc329c498ee42 100644 --- a/test/Serialization/Recovery/crash-xref.swift +++ b/test/Serialization/Recovery/crash-xref.swift @@ -1,5 +1,6 @@ /// Test xref error description by removing a type from a module after building -/// a client. +/// a client. This test disables deserialization recovery to hit a crash +/// reliably. // RUN: %empty-directory(%t) // RUN: %empty-directory(%t/partials) // RUN: %empty-directory(%t/normal) @@ -34,11 +35,9 @@ // NORMALFAILURE-LABEL: *** new swiftmodule files from the SDK and keep only swiftinterfaces. *** // NORMALFAILURE-NEXT: module 'Client', builder version {{.*}}', built from source, resilient, loaded from // NORMALFAILURE-NEXT: Could not deserialize type for 'foo()' -// NORMALFAILURE-NEXT: Caused by: top-level value not found +// NORMALFAILURE-NEXT: Caused by: modularization issue on 'SomeType', reference from 'Client' not resolvable: expected in 'A' but found in 'B' // NORMALFAILURE-NEXT: Cross-reference to module 'A' // NORMALFAILURE-NEXT: ... SomeType -// NORMALFAILURE-NEXT: Notes: -// NORMALFAILURE-NEXT: * There is a matching 'SomeType' in module 'B'. If this is imported from clang, please make sure the header is part of a single clang module. // RUN: cat %t/error_stderr | %FileCheck %s -check-prefixes=ALLOWFAILURE // ALLOWFAILURE-LABEL: *** DESERIALIZATION FAILURE *** @@ -46,11 +45,9 @@ // ALLOWFAILURE-LABEL: *** new swiftmodule files from the SDK and keep only swiftinterfaces. *** // ALLOWFAILURE-NEXT: module 'Client', builder version {{.*}}', built from source, non-resilient, built with -experimental-allow-module-with-compiler-errors, loaded from // ALLOWFAILURE-NEXT: Could not deserialize type for 'foo()' -// ALLOWFAILURE-NEXT: Caused by: top-level value not found +// ALLOWFAILURE-NEXT: Caused by: modularization issue on 'SomeType', reference from 'Client' not resolvable: expected in 'A' but found in 'B' // ALLOWFAILURE-NEXT: Cross-reference to module 'A' // ALLOWFAILURE-NEXT: ... SomeType -// ALLOWFAILURE-NEXT: Notes: -// ALLOWFAILURE-NEXT: * There is a matching 'SomeType' in module 'B'. If this is imported from clang, please make sure the header is part of a single clang module. /// Test a swiftmodule rebuilt from the swiftinterface. // RUN: not --crash %target-swift-frontend -emit-sil %t/cache/Client-*.swiftmodule -module-name Client -I %t/partials -disable-deserialization-recovery 2> %t/cache_stderr diff --git a/test/Serialization/modularization-error.swift b/test/Serialization/modularization-error.swift new file mode 100644 index 0000000000000..2c0ae39b05bec --- /dev/null +++ b/test/Serialization/modularization-error.swift @@ -0,0 +1,58 @@ +/// Simulate typical modularization issues using Swift modules. +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +/// Compile two library modules A and B, and a client. +// RUN: %target-swift-frontend %t/LibOriginal.swift -emit-module-path %t/A.swiftmodule -module-name A +// RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/B.swiftmodule -module-name B +// RUN: %target-swift-frontend %t/LibWithXRef.swift -emit-module-path %t/LibWithXRef.swiftmodule -module-name LibWithXRef -I %t + +/// Move MyType from A to B. +// RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/A.swiftmodule -module-name A +// RUN: %target-swift-frontend %t/LibOriginal.swift -emit-module-path %t/B.swiftmodule -module-name B +// RUN: not %target-swift-frontend -emit-sil %t/LibWithXRef.swiftmodule -module-name LibWithXRef -I %t 2>&1 \ +// RUN: | %FileCheck --check-prefixes CHECK,CHECK-MOVED %s +// CHECK-MOVED: LibWithXRef.swiftmodule:1:1: error: reference to type 'MyType' broken by a context change; 'MyType' was expected to be in 'A', but now a candidate is found only in 'B' + +/// Change MyType into a function. +// RUN: %target-swift-frontend %t/LibTypeChanged.swift -emit-module-path %t/A.swiftmodule -module-name A +// RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/B.swiftmodule -module-name B +// RUN: not %target-swift-frontend -emit-sil %t/LibWithXRef.swiftmodule -module-name LibWithXRef -I %t 2>&1 \ +// RUN: | %FileCheck --check-prefixes CHECK,CHECK-KIND-CHANGED %s +// CHECK-KIND-CHANGED: LibWithXRef.swiftmodule:1:1: error: reference to type 'MyType' broken by a context change; the details of 'MyType' from 'A' changed since building 'LibWithXRef' + +/// Change MyType into a function and move it. +// RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/A.swiftmodule -module-name A +// RUN: %target-swift-frontend %t/LibTypeChanged.swift -emit-module-path %t/B.swiftmodule -module-name B +// RUN: not %target-swift-frontend -emit-sil %t/LibWithXRef.swiftmodule -module-name LibWithXRef -I %t 2>&1 \ +// RUN: | %FileCheck --check-prefixes CHECK,CHECK-KIND-CHANGED-AND-MOVED %s +// CHECK-KIND-CHANGED-AND-MOVED: LibWithXRef.swiftmodule:1:1: error: reference to type 'MyType' broken by a context change; the details of 'MyType' changed since building 'LibWithXRef', it was in 'A' and is now found in 'B' + +/// Remove MyType from all imported modules. +// RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/A.swiftmodule -module-name A +// RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/B.swiftmodule -module-name B +// RUN: not %target-swift-frontend -emit-sil %t/LibWithXRef.swiftmodule -module-name LibWithXRef -I %t 2>&1 \ +// RUN: | %FileCheck --check-prefixes CHECK,CHECK-NOT-FOUND %s +// CHECK-NOT-FOUND: LibWithXRef.swiftmodule:1:1: error: reference to type 'MyType' broken by a context change; 'MyType' is not found, it was expected to be in 'A' + +// CHECK: LibWithXRef.swiftmodule:1:1: note: could not deserialize extension + +//--- Empty.swift +//--- LibOriginal.swift +public struct MyType { + public init() {} +} + +//--- LibTypeChanged.swift +/// Make it a function to fail filtering. +public func MyType() {} + +//--- LibWithXRef.swift +import A +import B + +public protocol Proto {} +extension MyType : Proto {} + +@available(SwiftStdlib 5.1, *) +public func foo() -> some Proto { return MyType() }