diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index d4e1c45a35f..de3e2da5911 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -442,7 +442,7 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc, }); auto originKind = getDisallowedOriginKind(ext, where); - if (originKind == DisallowedOriginKind::None) + if (where.canReferenceOrigin(originKind)) return false; auto reason = where.getExportabilityReason(); diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index bb799e6c653..e1d496a26d2 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -16,6 +16,7 @@ #include "TypeCheckAvailability.h" #include "MiscDiagnostics.h" +#include "TypeCheckAccess.h" #include "TypeCheckConcurrency.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" @@ -234,6 +235,20 @@ bool ExportContext::mustOnlyReferenceExportedDecls() const { return Exported || FragileKind.kind != FragileFunctionKind::None; } +bool ExportContext::canReferenceOrigin(DisallowedOriginKind originKind) const { + if (originKind == DisallowedOriginKind::None) + return true; + + // Non public imports aren't hidden dependencies in embedded mode, + // don't enforce them on implicitly always emit into client code. + if (originKind == DisallowedOriginKind::NonPublicImport && + getFragileFunctionKind().kind == + FragileFunctionKind::EmbeddedAlwaysEmitIntoClient) + return true; + + return false; +} + std::optional ExportContext::getExportabilityReason() const { if (Exported) diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index ce74c185e80..87ed2555754 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -38,6 +38,7 @@ namespace swift { class TypeRepr; class UnsafeUse; class ValueDecl; + enum class DisallowedOriginKind : uint8_t; enum class DeclAvailabilityFlag : uint8_t { /// Do not diagnose uses of protocols in versions before they were introduced. @@ -192,6 +193,10 @@ public: /// because it is the function body context of an inlinable function. bool mustOnlyReferenceExportedDecls() const; + /// If true, the context reference a dependency of \p originKind without + /// restriction. + bool canReferenceOrigin(DisallowedOriginKind originKind) const; + /// Get the ExportabilityReason for diagnostics. If this is 'None', there /// are no restrictions on referencing unexported declarations. std::optional getExportabilityReason() const; diff --git a/test/Sema/restricted-import-embedded-inlinable-conformances.swift b/test/Sema/restricted-import-embedded-inlinable-conformances.swift new file mode 100644 index 00000000000..cf955055c68 --- /dev/null +++ b/test/Sema/restricted-import-embedded-inlinable-conformances.swift @@ -0,0 +1,237 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/NormalLibrary.swiftmodule \ +// RUN: %S/Inputs/implementation-only-import-in-decls-public-helper.swift \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ +// RUN: -enable-experimental-feature Embedded +// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule \ +// RUN: %S/Inputs/implementation-only-import-in-decls-helper.swift -I %t \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ +// RUN: -enable-experimental-feature Embedded + +/// Access-level on imports allow references from implicitly inlinable code. +// RUN: %target-typecheck-verify-swift -I %t \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ +// RUN: -enable-experimental-feature Embedded \ +// RUN: -verify-additional-prefix access-level- + +/// @_implementationOnly rejects references from implicitly inlinable code. +// RUN: %target-typecheck-verify-swift -I %t \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ +// RUN: -enable-experimental-feature Embedded \ +// RUN: -D IOI -verify-additional-prefix ioi- + +// REQUIRES: swift_feature_Embedded +// REQUIRES: embedded_stdlib_cross_compiling + +#if IOI +@_implementationOnly import BADLibrary // expected-ioi-warning {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}} +#else +internal import BADLibrary // expected-access-level-note 35 {{imported as 'internal' from 'BADLibrary' here}} +#endif +import NormalLibrary + +public typealias NormalProtoAssoc = T.Assoc +@inlinable func testConformanceInTypealias() { + let x: NormalProtoAssoc? = nil // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + _ = x + _ = NormalProtoAssoc() // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} +} + +func internalConformanceInTypealias() { + let x: NormalProtoAssoc? = nil // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}} + _ = x + _ = NormalProtoAssoc() // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}} +} + +@_neverEmitIntoClient +func internalConformanceInTypealiasNEIC() { + let x: NormalProtoAssoc? = nil // okay + _ = x + _ = NormalProtoAssoc() // okay +} + +public struct NormalProtoAssocHolder { + public var value: T.Assoc? + public init() {} + public init(_ value: T?) {} +} +@inlinable func testConformanceInBoundGeneric() { + let x: NormalProtoAssocHolder? = nil // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + _ = x + // FIXME: We get this error twice: once for the TypeExpr and once for the implicit init. + _ = NormalProtoAssocHolder() // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + _ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-error 2{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} +} + +func internalConformanceInBoundGeneric() { + let x: NormalProtoAssocHolder? = nil // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}} + _ = x + _ = NormalProtoAssocHolder() // expected-ioi-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}} + _ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-ioi-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}} +} + +@_neverEmitIntoClient +func internalConformanceInBoundGenericNEIC() { + let x: NormalProtoAssocHolder? = nil // okay + _ = x + _ = NormalProtoAssocHolder() // okay + _ = NormalProtoAssocHolder(nil as NormalStruct?) // okay +} + +@inlinable func testDowncast(_ x: Any) -> Bool { + let normal = x is NormalProtoAssocHolder // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + let alias = x is NormalProtoAssoc // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + return normal || alias +} + +func internalDowncast(_ x: Any) -> Bool { + let normal = x is NormalProtoAssocHolder // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} + let alias = x is NormalProtoAssoc // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} + return normal || alias +} + +@_neverEmitIntoClient +func internalDowncastNEIC(_ x: Any) -> Bool { + let normal = x is NormalProtoAssocHolder // okay + let alias = x is NormalProtoAssoc // okay + return normal || alias +} + +@inlinable func testSwitch(_ x: Any) { + switch x { + case let holder as NormalProtoAssocHolder: // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + _ = holder + break + case is NormalProtoAssoc: // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + break + default: + break + } +} + +func internalSwitch(_ x: Any) { + switch x { + case let holder as NormalProtoAssocHolder: // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} + _ = holder + break + case is NormalProtoAssoc: // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} + break + default: + break + } +} + +@_neverEmitIntoClient +func internalSwitchNEIC(_ x: Any) { + switch x { + case let holder as NormalProtoAssocHolder: // okay + _ = holder + break + case is NormalProtoAssoc: // okay + break + default: + break + } +} + +public enum NormalProtoEnumUser { + case a +} + +@inlinable func testEnum() { + // FIXME: We get this error twice: once for the pattern and once for the implicit TypeExpr. + let x: NormalProtoEnumUser = .a // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + _ = x + // FIXME: We get this error twice: once for the TypeExpr and once for the case. + _ = NormalProtoEnumUser.a // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} +} + +func internalEnum() { + let x: NormalProtoEnumUser = .a // expected-ioi-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} + _ = x + _ = NormalProtoEnumUser.a // expected-ioi-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} +} + +@_neverEmitIntoClient +func internalEnumNEIC() { + let x: NormalProtoEnumUser = .a // okay + _ = x + _ = NormalProtoEnumUser.a // okay +} + +@usableFromInline func testFuncImpl(_: T.Type) {} + +@inlinable func testFunc() { + testFuncImpl(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} +} + +func internalFunc() { + testFuncImpl(NormalStruct.self) // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} +} + +@_neverEmitIntoClient +func internalFuncNEIC() { + testFuncImpl(NormalStruct.self) // okay +} + +public struct ForTestingMembers { + public init() {} + public init(_: T.Type) {} + + public subscript(_: T.Type) -> Int { + get { return 0 } + set {} + } + + public func method(_: T.Type) {} +} + +@inlinable func testMembers() { + _ = ForTestingMembers(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + _ = ForTestingMembers.init(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + + _ = ForTestingMembers()[NormalStruct.self] // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + var instance = ForTestingMembers() + instance[NormalStruct.self] = 1 // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + + ForTestingMembers().method(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} +} + +extension NormalProtoAssocHolder { + public static func testAnotherConformance(_: U.Type) {} +} + +@inlinable func testMultipleConformances() { + NormalProtoAssocHolder.testAnotherConformance(NormalClass.self) + // expected-error@-1 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + // expected-error@-2 {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary'}} +} + +@inlinable func localTypeAlias() { + typealias LocalUser = NormalProtoAssocHolder // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + typealias LocalGenericUser = (T, NormalProtoAssocHolder) // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + + typealias LocalProtoAssoc = T.Assoc + _ = LocalProtoAssoc() // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} +} + +@inlinable func localFunctions() { + func local(_: NormalProtoAssocHolder) {} // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + func localReturn() -> NormalProtoAssocHolder { fatalError() } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + let _ = { (_: NormalProtoAssocHolder) in return } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + let _ = { () -> NormalProtoAssocHolder in fatalError() } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} +} + +@inlinable public func signatureOfInlinable(_: NormalProtoAssocHolder) {} // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + +public func testDefaultArgument(_: Int = NormalProtoAssoc()) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}} + + +public class SubclassOfNormalClass: NormalClass {} + +@inlinable public func testInheritedConformance() { + _ = NormalProtoAssocHolder.self // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary'}} +} +@inlinable public func testSpecializedConformance() { + _ = NormalProtoAssocHolder>.self // expected-error {{cannot use conformance of 'GenericStruct' to 'NormalProto' here; 'BADLibrary'}} +}