Skip to content

Commit ed85a12

Browse files
authored
Merge pull request #85317 from xymus/non-neic-funcs-and-typealiases
Sema: Allow more embedded code to reference non-public imports
2 parents 5f91e49 + e04dac5 commit ed85a12

6 files changed

+267
-2
lines changed

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc,
174174
auto ignoredDowngradeToWarning = DowngradeToWarning::No;
175175
auto originKind =
176176
getDisallowedOriginKind(D, where, ignoredDowngradeToWarning);
177-
if (originKind == DisallowedOriginKind::None)
177+
if (where.canReferenceOrigin(originKind))
178178
return false;
179179

180180
// As an exception, if the import of the module that defines the desugared
@@ -443,7 +443,7 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
443443
});
444444

445445
auto originKind = getDisallowedOriginKind(ext, where);
446-
if (originKind == DisallowedOriginKind::None)
446+
if (where.canReferenceOrigin(originKind))
447447
return false;
448448

449449
auto reason = where.getExportabilityReason();

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "TypeCheckAvailability.h"
1818
#include "MiscDiagnostics.h"
19+
#include "TypeCheckAccess.h"
1920
#include "TypeCheckConcurrency.h"
2021
#include "TypeCheckObjC.h"
2122
#include "TypeCheckType.h"
@@ -234,6 +235,20 @@ bool ExportContext::mustOnlyReferenceExportedDecls() const {
234235
return Exported || FragileKind.kind != FragileFunctionKind::None;
235236
}
236237

238+
bool ExportContext::canReferenceOrigin(DisallowedOriginKind originKind) const {
239+
if (originKind == DisallowedOriginKind::None)
240+
return true;
241+
242+
// Non public imports aren't hidden dependencies in embedded mode,
243+
// don't enforce them on implicitly always emit into client code.
244+
if (originKind == DisallowedOriginKind::NonPublicImport &&
245+
getFragileFunctionKind().kind ==
246+
FragileFunctionKind::EmbeddedAlwaysEmitIntoClient)
247+
return true;
248+
249+
return false;
250+
}
251+
237252
std::optional<ExportabilityReason>
238253
ExportContext::getExportabilityReason() const {
239254
if (Exported)

lib/Sema/TypeCheckAvailability.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ namespace swift {
3838
class TypeRepr;
3939
class UnsafeUse;
4040
class ValueDecl;
41+
enum class DisallowedOriginKind : uint8_t;
4142

4243
enum class DeclAvailabilityFlag : uint8_t {
4344
/// Do not diagnose uses of protocols in versions before they were introduced.
@@ -192,6 +193,10 @@ class ExportContext {
192193
/// because it is the function body context of an inlinable function.
193194
bool mustOnlyReferenceExportedDecls() const;
194195

196+
/// If true, the context reference a dependency of \p originKind without
197+
/// restriction.
198+
bool canReferenceOrigin(DisallowedOriginKind originKind) const;
199+
195200
/// Get the ExportabilityReason for diagnostics. If this is 'None', there
196201
/// are no restrictions on referencing unexported declarations.
197202
std::optional<ExportabilityReason> getExportabilityReason() const;

test/Sema/access-level-import-embedded.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import indirects
2424

2525
internal func localInternalFunc() {} // expected-note {{global function 'localInternalFunc()' is not '@usableFromInline' or public}}
2626

27+
typealias AliasToDirect = StructFromDirect
28+
2729
@inlinable
2830
public func explicitlyInlinable(arg: StructFromDirect = StructFromDirect()) {
2931
// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}}
@@ -75,6 +77,8 @@ public func implicitlyInlinablePublic(arg: StructFromDirect = StructFromDirect()
7577
implicitlyInlinablePublic()
7678
implicitlyInlinablePrivate()
7779
explicitNonInliable()
80+
81+
let _: AliasToDirect
7882
}
7983

8084
internal func implicitlyInlinablePrivate(arg: StructFromDirect = StructFromDirect()) {

test/Sema/implementation-only-import-embedded.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ internal func localInternalFunc() {} // expected-note {{global function 'localIn
2626

2727
@_spi(S) public func localSPI() {}
2828

29+
typealias AliasToDirect = StructFromDirect
30+
2931
@inlinable
3032
public func explicitlyInlinable(arg: StructFromDirect = StructFromDirect()) {
3133
// expected-error @-1 {{initializer 'init()' cannot be used in a default argument value because 'directs' was imported implementation-only}}
@@ -82,6 +84,8 @@ public func implicitlyInlinablePublic(arg: StructFromDirect = StructFromDirect()
8284

8385
localSPI()
8486
spiFunctionFromDirect()
87+
88+
let _: AliasToDirect // expected-error {{AliasToDirect' aliases 'directs.StructFromDirect' and cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' has been imported as implementation-only}}
8589
}
8690

8791
private func implicitlyInlinablePrivate(arg: StructFromDirect = StructFromDirect()) {
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t/NormalLibrary.swiftmodule \
3+
// RUN: %S/Inputs/implementation-only-import-in-decls-public-helper.swift \
4+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
5+
// RUN: -enable-experimental-feature Embedded
6+
// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule \
7+
// RUN: %S/Inputs/implementation-only-import-in-decls-helper.swift -I %t \
8+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
9+
// RUN: -enable-experimental-feature Embedded
10+
11+
/// Access-level on imports allow references from implicitly inlinable code.
12+
// RUN: %target-typecheck-verify-swift -I %t \
13+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
14+
// RUN: -enable-experimental-feature Embedded \
15+
// RUN: -verify-additional-prefix access-level-
16+
17+
/// @_implementationOnly rejects references from implicitly inlinable code.
18+
// RUN: %target-typecheck-verify-swift -I %t \
19+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
20+
// RUN: -enable-experimental-feature Embedded \
21+
// RUN: -D IOI -verify-additional-prefix ioi-
22+
23+
// REQUIRES: swift_feature_Embedded
24+
// REQUIRES: embedded_stdlib_cross_compiling
25+
26+
#if IOI
27+
@_implementationOnly import BADLibrary // expected-ioi-warning {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}}
28+
#else
29+
internal import BADLibrary // expected-access-level-note 35 {{imported as 'internal' from 'BADLibrary' here}}
30+
#endif
31+
import NormalLibrary
32+
33+
public typealias NormalProtoAssoc<T: NormalProto> = T.Assoc
34+
@inlinable func testConformanceInTypealias() {
35+
let x: NormalProtoAssoc<NormalStruct>? = nil // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
36+
_ = x
37+
_ = NormalProtoAssoc<NormalStruct>() // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
38+
}
39+
40+
func internalConformanceInTypealias() {
41+
let x: NormalProtoAssoc<NormalStruct>? = nil // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}}
42+
_ = x
43+
_ = NormalProtoAssoc<NormalStruct>() // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}}
44+
}
45+
46+
@_neverEmitIntoClient
47+
func internalConformanceInTypealiasNEIC() {
48+
let x: NormalProtoAssoc<NormalStruct>? = nil // okay
49+
_ = x
50+
_ = NormalProtoAssoc<NormalStruct>() // okay
51+
}
52+
53+
public struct NormalProtoAssocHolder<T: NormalProto> {
54+
public var value: T.Assoc?
55+
public init() {}
56+
public init(_ value: T?) {}
57+
}
58+
@inlinable func testConformanceInBoundGeneric() {
59+
let x: NormalProtoAssocHolder<NormalStruct>? = nil // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
60+
_ = x
61+
// FIXME: We get this error twice: once for the TypeExpr and once for the implicit init.
62+
_ = NormalProtoAssocHolder<NormalStruct>() // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
63+
_ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-error 2{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
64+
}
65+
66+
func internalConformanceInBoundGeneric() {
67+
let x: NormalProtoAssocHolder<NormalStruct>? = nil // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}}
68+
_ = x
69+
_ = NormalProtoAssocHolder<NormalStruct>() // expected-ioi-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}}
70+
_ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-ioi-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}}
71+
}
72+
73+
@_neverEmitIntoClient
74+
func internalConformanceInBoundGenericNEIC() {
75+
let x: NormalProtoAssocHolder<NormalStruct>? = nil // okay
76+
_ = x
77+
_ = NormalProtoAssocHolder<NormalStruct>() // okay
78+
_ = NormalProtoAssocHolder(nil as NormalStruct?) // okay
79+
}
80+
81+
@inlinable func testDowncast(_ x: Any) -> Bool {
82+
let normal = x is NormalProtoAssocHolder<NormalStruct> // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
83+
let alias = x is NormalProtoAssoc<NormalStruct> // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
84+
return normal || alias
85+
}
86+
87+
func internalDowncast(_ x: Any) -> Bool {
88+
let normal = x is NormalProtoAssocHolder<NormalStruct> // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
89+
let alias = x is NormalProtoAssoc<NormalStruct> // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
90+
return normal || alias
91+
}
92+
93+
@_neverEmitIntoClient
94+
func internalDowncastNEIC(_ x: Any) -> Bool {
95+
let normal = x is NormalProtoAssocHolder<NormalStruct> // okay
96+
let alias = x is NormalProtoAssoc<NormalStruct> // okay
97+
return normal || alias
98+
}
99+
100+
@inlinable func testSwitch(_ x: Any) {
101+
switch x {
102+
case let holder as NormalProtoAssocHolder<NormalStruct>: // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
103+
_ = holder
104+
break
105+
case is NormalProtoAssoc<NormalStruct>: // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
106+
break
107+
default:
108+
break
109+
}
110+
}
111+
112+
func internalSwitch(_ x: Any) {
113+
switch x {
114+
case let holder as NormalProtoAssocHolder<NormalStruct>: // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
115+
_ = holder
116+
break
117+
case is NormalProtoAssoc<NormalStruct>: // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
118+
break
119+
default:
120+
break
121+
}
122+
}
123+
124+
@_neverEmitIntoClient
125+
func internalSwitchNEIC(_ x: Any) {
126+
switch x {
127+
case let holder as NormalProtoAssocHolder<NormalStruct>: // okay
128+
_ = holder
129+
break
130+
case is NormalProtoAssoc<NormalStruct>: // okay
131+
break
132+
default:
133+
break
134+
}
135+
}
136+
137+
public enum NormalProtoEnumUser<T: NormalProto> {
138+
case a
139+
}
140+
141+
@inlinable func testEnum() {
142+
// FIXME: We get this error twice: once for the pattern and once for the implicit TypeExpr.
143+
let x: NormalProtoEnumUser<NormalStruct> = .a // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
144+
_ = x
145+
// FIXME: We get this error twice: once for the TypeExpr and once for the case.
146+
_ = NormalProtoEnumUser<NormalStruct>.a // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
147+
}
148+
149+
func internalEnum() {
150+
let x: NormalProtoEnumUser<NormalStruct> = .a // expected-ioi-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
151+
_ = x
152+
_ = NormalProtoEnumUser<NormalStruct>.a // expected-ioi-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
153+
}
154+
155+
@_neverEmitIntoClient
156+
func internalEnumNEIC() {
157+
let x: NormalProtoEnumUser<NormalStruct> = .a // okay
158+
_ = x
159+
_ = NormalProtoEnumUser<NormalStruct>.a // okay
160+
}
161+
162+
@usableFromInline func testFuncImpl<T: NormalProto>(_: T.Type) {}
163+
164+
@inlinable func testFunc() {
165+
testFuncImpl(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
166+
}
167+
168+
func internalFunc() {
169+
testFuncImpl(NormalStruct.self) // expected-ioi-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
170+
}
171+
172+
@_neverEmitIntoClient
173+
func internalFuncNEIC() {
174+
testFuncImpl(NormalStruct.self) // okay
175+
}
176+
177+
public struct ForTestingMembers {
178+
public init() {}
179+
public init<T: NormalProto>(_: T.Type) {}
180+
181+
public subscript<T: NormalProto>(_: T.Type) -> Int {
182+
get { return 0 }
183+
set {}
184+
}
185+
186+
public func method<T: NormalProto>(_: T.Type) {}
187+
}
188+
189+
@inlinable func testMembers() {
190+
_ = ForTestingMembers(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
191+
_ = ForTestingMembers.init(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
192+
193+
_ = ForTestingMembers()[NormalStruct.self] // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
194+
var instance = ForTestingMembers()
195+
instance[NormalStruct.self] = 1 // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
196+
197+
ForTestingMembers().method(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
198+
}
199+
200+
extension NormalProtoAssocHolder {
201+
public static func testAnotherConformance<U: NormalProto>(_: U.Type) {}
202+
}
203+
204+
@inlinable func testMultipleConformances() {
205+
NormalProtoAssocHolder<NormalStruct>.testAnotherConformance(NormalClass.self)
206+
// expected-error@-1 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
207+
// expected-error@-2 {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary'}}
208+
}
209+
210+
@inlinable func localTypeAlias() {
211+
typealias LocalUser = NormalProtoAssocHolder<NormalStruct> // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
212+
typealias LocalGenericUser<T> = (T, NormalProtoAssocHolder<NormalStruct>) // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
213+
214+
typealias LocalProtoAssoc<T: NormalProto> = T.Assoc
215+
_ = LocalProtoAssoc<NormalStruct>() // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
216+
}
217+
218+
@inlinable func localFunctions() {
219+
func local(_: NormalProtoAssocHolder<NormalStruct>) {} // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
220+
func localReturn() -> NormalProtoAssocHolder<NormalStruct> { fatalError() } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
221+
let _ = { (_: NormalProtoAssocHolder<NormalStruct>) in return } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
222+
let _ = { () -> NormalProtoAssocHolder<NormalStruct> in fatalError() } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
223+
}
224+
225+
@inlinable public func signatureOfInlinable(_: NormalProtoAssocHolder<NormalStruct>) {} // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
226+
227+
public func testDefaultArgument(_: Int = NormalProtoAssoc<NormalStruct>()) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary'}}
228+
229+
230+
public class SubclassOfNormalClass: NormalClass {}
231+
232+
@inlinable public func testInheritedConformance() {
233+
_ = NormalProtoAssocHolder<SubclassOfNormalClass>.self // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary'}}
234+
}
235+
@inlinable public func testSpecializedConformance() {
236+
_ = NormalProtoAssocHolder<GenericStruct<Int>>.self // expected-error {{cannot use conformance of 'GenericStruct<T>' to 'NormalProto' here; 'BADLibrary'}}
237+
}

0 commit comments

Comments
 (0)