diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 130b1cb2a9812..4bf6bdbdc3c87 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1483,14 +1483,11 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, } }; -// Diagnose public APIs exposing types that are either imported as -// implementation-only or declared as SPI. class ExportabilityChecker : public DeclVisitor { class Diagnoser; void checkTypeImpl( Type type, const TypeRepr *typeRepr, const SourceFile &SF, - const Decl *context, const Diagnoser &diagnoser) { // Don't bother checking errors. if (type && type->hasError()) @@ -1503,15 +1500,14 @@ class ExportabilityChecker : public DeclVisitor { if (typeRepr) { const_cast(typeRepr)->walk(TypeReprIdentFinder( [&](const ComponentIdentTypeRepr *component) { - TypeDecl *typeDecl = component->getBoundDecl(); - ModuleDecl *M = typeDecl->getModuleContext(); - bool isImplementationOnly = SF.isImportedImplementationOnly(M); - if (isImplementationOnly || - (SF.isImportedAsSPI(typeDecl) && !context->isSPI())) { - diagnoser.diagnoseType(typeDecl, component, isImplementationOnly); - foundAnyIssues = true; - } - + ModuleDecl *M = component->getBoundDecl()->getModuleContext(); + if (!SF.isImportedImplementationOnly(M) && + !SF.isImportedAsSPI(component->getBoundDecl())) + return true; + + diagnoser.diagnoseType(component->getBoundDecl(), component, + SF.isImportedImplementationOnly(M)); + foundAnyIssues = true; // We still continue even in the diagnostic case to report multiple // violations. return true; @@ -1529,19 +1525,19 @@ class ExportabilityChecker : public DeclVisitor { class ProblematicTypeFinder : public TypeDeclFinder { const SourceFile &SF; - const Decl *context; const Diagnoser &diagnoser; public: - ProblematicTypeFinder(const SourceFile &SF, const Decl *context, const Diagnoser &diagnoser) - : SF(SF), context(context), diagnoser(diagnoser) {} + ProblematicTypeFinder(const SourceFile &SF, const Diagnoser &diagnoser) + : SF(SF), diagnoser(diagnoser) {} void visitTypeDecl(const TypeDecl *typeDecl) { ModuleDecl *M = typeDecl->getModuleContext(); - bool isImplementationOnly = SF.isImportedImplementationOnly(M); - if (isImplementationOnly || - (SF.isImportedAsSPI(typeDecl) && !context->isSPI())) - diagnoser.diagnoseType(typeDecl, /*typeRepr*/nullptr, - isImplementationOnly); + if (!SF.isImportedImplementationOnly(M) && + !SF.isImportedAsSPI(typeDecl)) + return; + + diagnoser.diagnoseType(typeDecl, /*typeRepr*/nullptr, + SF.isImportedImplementationOnly(M)); } void visitSubstitutionMap(SubstitutionMap subs) { @@ -1601,7 +1597,7 @@ class ExportabilityChecker : public DeclVisitor { } }; - type.walk(ProblematicTypeFinder(SF, context, diagnoser)); + type.walk(ProblematicTypeFinder(SF, diagnoser)); } void checkType( @@ -1609,7 +1605,7 @@ class ExportabilityChecker : public DeclVisitor { const Diagnoser &diagnoser) { auto *SF = context->getDeclContext()->getParentSourceFile(); assert(SF && "checking a non-source declaration?"); - return checkTypeImpl(type, typeRepr, *SF, context, diagnoser); + return checkTypeImpl(type, typeRepr, *SF, diagnoser); } void checkType( @@ -1706,7 +1702,7 @@ class ExportabilityChecker : public DeclVisitor { AccessScope accessScope = VD->getFormalAccessScope(nullptr, /*treatUsableFromInlineAsPublic*/true); - if (accessScope.isPublic()) + if (accessScope.isPublic() && !accessScope.isSPI()) return false; // Is this a stored property in a non-resilient struct or class? diff --git a/test/SPI/implementation_only_spi_import_exposability.swift b/test/SPI/implementation_only_spi_import_exposability.swift deleted file mode 100644 index aa227d7704351..0000000000000 --- a/test/SPI/implementation_only_spi_import_exposability.swift +++ /dev/null @@ -1,46 +0,0 @@ -/// @_implementationOnly imported decls (SPI or not) should not be exposed in SPI. - -// RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -DLIB %s -module-name Lib -emit-module-path %t/Lib.swiftmodule -// RUN: %target-typecheck-verify-swift -DCLIENT -I %t - -#if LIB - -@_spi(A) public func spiFunc() {} - -@_spi(A) public struct SPIStruct { - public init() {} -} - -@_spi(A) public protocol SPIProtocol {} - -public func ioiFunc() {} - -public struct IOIStruct { - public init() {} -} - -public protocol IOIProtocol {} - -#elseif CLIENT - -@_spi(A) @_implementationOnly import Lib - -@_spi(B) public func leakSPIStruct(_ a: SPIStruct) -> SPIStruct { fatalError() } // expected-error 2 {{cannot use struct 'SPIStruct' here; 'Lib' has been imported as implementation-only}} -@_spi(B) public func leakIOIStruct(_ a: IOIStruct) -> IOIStruct { fatalError() } // expected-error 2 {{cannot use struct 'IOIStruct' here; 'Lib' has been imported as implementation-only}} - -public struct PublicStruct : IOIProtocol, SPIProtocol { // expected-error {{cannot use protocol 'IOIProtocol' here; 'Lib' has been imported as implementation-only}} -// expected-error @-1 {{cannot use protocol 'SPIProtocol' here; 'Lib' has been imported as implementation-only}} - public var spiStruct = SPIStruct() // expected-error {{cannot use struct 'SPIStruct' here; 'Lib' has been imported as implementation-only}} - public var ioiStruct = IOIStruct() // expected-error {{cannot use struct 'IOIStruct' here; 'Lib' has been imported as implementation-only}} - - @inlinable - public func publicInlinable() { - spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} - ioiFunc() // expected-error {{global function 'ioiFunc()' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} - let s = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from an '@inlinable' function}} - let i = IOIStruct() // expected-error {{struct 'IOIStruct' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} - } -} - -#endif