Skip to content

Commit bda28ff

Browse files
authored
Require @usableFromInline on witnesses for non-type requirements too (#20485)
Follow-up to f33bf67 for non-type requirements. We use non-type witnesses for optimization purposes, so if we didn't enforce this we might end up with something silently performing worse with parseable interfaces than it would with swiftmodules. There's also a tricky case where the client of the interface infers a different implementation: public protocol Foo { func foo() } extension Foo { public func foo() { print("default") } } @usableFromInline struct FooImpl: Foo { internal func foo() { print("actual") } } There might be another solution to this in the future, but for now this is the simplest answer. Like f33bf67, it'll be a warning in Swift 4 mode and an error in Swift 5 mode. rdar://problem/43824161
1 parent dd11305 commit bda28ff

File tree

4 files changed

+24
-0
lines changed

4 files changed

+24
-0
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,13 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
13221322
return RequirementCheck(kind, getRequiredAccessScope());
13231323
}
13241324

1325+
if (isUsableFromInlineRequired()) {
1326+
bool witnessIsUsableFromInline = match.Witness->getFormalAccessScope(
1327+
DC, /*usableFromInlineAsPublic*/true).isPublic();
1328+
if (!witnessIsUsableFromInline)
1329+
return CheckKind::UsableFromInline;
1330+
}
1331+
13251332
auto requiredAvailability = AvailabilityContext::alwaysAvailable();
13261333
if (checkWitnessAvailability(requirement, match.Witness,
13271334
&requiredAvailability)) {
@@ -3067,6 +3074,10 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
30673074
break;
30683075
}
30693076

3077+
case CheckKind::UsableFromInline:
3078+
diagnoseOrDefer(requirement, false, DiagnoseUsableFromInline(witness));
3079+
break;
3080+
30703081
case CheckKind::Availability: {
30713082
diagnoseOrDefer(requirement, false,
30723083
[witness, requirement, check](

lib/Sema/TypeCheckProtocol.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ enum class CheckKind : unsigned {
252252
/// requirement.
253253
AccessOfSetter,
254254

255+
/// The witness needs to be @usableFromInline.
256+
UsableFromInline,
257+
255258
/// The witness is less available than the requirement.
256259
Availability,
257260

test/Compatibility/attr_usableFromInline_protocol.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@ public protocol PublicProtoWithReqs {
88

99
@usableFromInline struct UFIAdopter<T> : PublicProtoWithReqs {}
1010
// expected-warning@-1 {{type alias 'Assoc' should be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
11+
// expected-warning@-2 {{instance method 'foo()' should be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
1112
extension UFIAdopter {
1213
typealias Assoc = Int
1314
// expected-note@-1 {{'Assoc' declared here}}
1415
func foo() {}
16+
// expected-note@-1 {{'foo()' declared here}}
1517
}
1618

1719
@usableFromInline struct UFIAdopterAllInOne<T> : PublicProtoWithReqs {
1820
typealias Assoc = Int
1921
// expected-warning@-1 {{type alias 'Assoc' should be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
2022
func foo() {}
23+
// expected-warning@-1 {{instance method 'foo()' should be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
2124
}
2225

2326
internal struct InternalAdopter<T> : PublicProtoWithReqs {}
@@ -34,9 +37,11 @@ extension InternalAdopter {
3437

3538
public struct PublicAdopter<T> : UFIProtoWithReqs {}
3639
// expected-warning@-1 {{type alias 'Assoc' should be declared '@usableFromInline' because because it matches a requirement in protocol 'UFIProtoWithReqs'}} {{none}}
40+
// expected-warning@-2 {{instance method 'foo()' should be declared '@usableFromInline' because because it matches a requirement in protocol 'UFIProtoWithReqs'}} {{none}}
3741
extension PublicAdopter {
3842
typealias Assoc = Int
3943
// expected-note@-1 {{'Assoc' declared here}}
4044
func foo() {}
45+
// expected-note@-1 {{'foo()' declared here}}
4146
}
4247
extension InternalAdopter: UFIProtoWithReqs {} // okay

test/attr/attr_usableFromInline_protocol.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@ public protocol PublicProtoWithReqs {
88

99
@usableFromInline struct UFIAdopter<T> : PublicProtoWithReqs {}
1010
// expected-error@-1 {{type alias 'Assoc' must be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
11+
// expected-error@-2 {{instance method 'foo()' must be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
1112
extension UFIAdopter {
1213
typealias Assoc = Int
1314
// expected-note@-1 {{'Assoc' declared here}}
1415
func foo() {}
16+
// expected-note@-1 {{'foo()' declared here}}
1517
}
1618

1719
@usableFromInline struct UFIAdopterAllInOne<T> : PublicProtoWithReqs {
1820
typealias Assoc = Int
1921
// expected-error@-1 {{type alias 'Assoc' must be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
2022
func foo() {}
23+
// expected-error@-1 {{instance method 'foo()' must be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
2124
}
2225

2326
internal struct InternalAdopter<T> : PublicProtoWithReqs {}
@@ -34,9 +37,11 @@ extension InternalAdopter {
3437

3538
public struct PublicAdopter<T> : UFIProtoWithReqs {}
3639
// expected-error@-1 {{type alias 'Assoc' must be declared '@usableFromInline' because because it matches a requirement in protocol 'UFIProtoWithReqs'}} {{none}}
40+
// expected-error@-2 {{instance method 'foo()' must be declared '@usableFromInline' because because it matches a requirement in protocol 'UFIProtoWithReqs'}} {{none}}
3741
extension PublicAdopter {
3842
typealias Assoc = Int
3943
// expected-note@-1 {{'Assoc' declared here}}
4044
func foo() {}
45+
// expected-note@-1 {{'foo()' declared here}}
4146
}
4247
extension InternalAdopter: UFIProtoWithReqs {} // okay

0 commit comments

Comments
 (0)