Skip to content

Commit 9302cbe

Browse files
committed
Sema: Diagnose @dynamicMemberLookup subscripts that are not accessible enough.
Since this is a source breaking change, downgrade the diagnostic to a warning until the next language version unless library evolution is enabled, in which case this would have resulted in a broken `.swiftinterface` and it therefore makes sense to diagnose eagerly. Resolves rdar://156919010.
1 parent 597aaba commit 9302cbe

File tree

8 files changed

+195
-5
lines changed

8 files changed

+195
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,10 @@ ERROR(invalid_dynamic_member_lookup_type,none,
17301730
NOTE(invalid_dynamic_member_subscript, none,
17311731
"add an explicit argument label to this subscript to satisfy "
17321732
"the '@dynamicMemberLookup' requirement", ())
1733+
ERROR(dynamic_member_lookup_candidate_inaccessible,none,
1734+
"'@dynamicMemberLookup' requires %0 to be as accessible as its "
1735+
"enclosing type",
1736+
(ValueDecl *))
17331737

17341738
ERROR(string_index_not_integer,none,
17351739
"String must not be indexed with %0, it has variable size elements",

lib/Sema/TypeCheckAttr.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2141,6 +2141,36 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) {
21412141

21422142
if (candidates.empty()) {
21432143
emitInvalidTypeDiagnostic(oneCandidate->getLoc());
2144+
return;
2145+
}
2146+
2147+
// Diagnose if there are no candidates that are sufficiently accessible.
2148+
auto requiredAccessScope = decl->getFormalAccessScope(/*useDC=*/nullptr);
2149+
auto accessDC = requiredAccessScope.getDeclContext();
2150+
auto inaccessibleCandidate = candidates.front().getValueDecl();
2151+
candidates.filter([&](LookupResultEntry entry, bool isOuter) -> bool {
2152+
return entry.getValueDecl()->isAccessibleFrom(accessDC);
2153+
});
2154+
2155+
if (candidates.empty()) {
2156+
auto futureVersion = version::Version::getFutureMajorLanguageVersion();
2157+
bool shouldError = ctx.isSwiftVersionAtLeast(futureVersion);
2158+
2159+
// Diagnose as an error in resilient modules regardless of language
2160+
// version since this will break the swiftinterface.
2161+
shouldError |= decl->getModuleContext()->isResilient();
2162+
2163+
auto diag = diagnose(inaccessibleCandidate->getLoc(),
2164+
diag::dynamic_member_lookup_candidate_inaccessible,
2165+
inaccessibleCandidate);
2166+
fixItAccess(diag, inaccessibleCandidate,
2167+
requiredAccessScope.requiredAccessForDiagnostics());
2168+
diag.warnUntilSwiftVersionIf(!shouldError, futureVersion);
2169+
2170+
if (shouldError) {
2171+
attr->setInvalid();
2172+
return;
2173+
}
21442174
}
21452175

21462176
return;

test/SILOptimizer/diagnose_unreachable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ struct OuterStruct {
507507
}
508508

509509
@dynamicMemberLookup
510-
public enum DynamicLookupEnum {
510+
enum DynamicLookupEnum {
511511
subscript<T>(dynamicMember keyPath: KeyPath<OuterStruct, T>) -> T {
512512
fatalError()
513513
}

test/SILOptimizer/keypath_opt_crash.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010
public struct S {
1111
private let x: NSXPCConnection
1212

13-
subscript<T>(dynamicMember property: ReferenceWritableKeyPath<NSXPCConnection, T>) -> T {
13+
public subscript<T>(dynamicMember property: ReferenceWritableKeyPath<NSXPCConnection, T>) -> T {
1414
get {
1515
x[keyPath: property]
1616
}

test/attr/attr_dynamic_member_lookup.swift

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-feature KeyPathWithMethodMembers
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature KeyPathWithMethodMembers -verify-additional-prefix non-resilient-
2+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature KeyPathWithMethodMembers -enable-library-evolution -verify-additional-prefix resilient-
23
// REQUIRES: swift_feature_KeyPathWithMethodMembers
34

45
var global = 42
@@ -198,6 +199,86 @@ class InvalidDerived : InvalidBase { subscript(dynamicMember: String) -> Int { g
198199
// expected-error @+1 {{value of type 'InvalidDerived' has no member 'dynamicallyLookedUp'}}
199200
_ = InvalidDerived().dynamicallyLookedUp
200201

202+
//===----------------------------------------------------------------------===//
203+
// Access level
204+
//===----------------------------------------------------------------------===//
205+
206+
@dynamicMemberLookup
207+
public struct Accessible1 {
208+
public subscript(dynamicMember member: String) -> Int {
209+
return 42
210+
}
211+
}
212+
213+
@dynamicMemberLookup
214+
private struct Accessible2 {
215+
fileprivate subscript(dynamicMember member: String) -> Int {
216+
return 42
217+
}
218+
}
219+
220+
@dynamicMemberLookup
221+
open class Accessible3 {
222+
public subscript(dynamicMember member: String) -> Int {
223+
return 42
224+
}
225+
}
226+
227+
@dynamicMemberLookup
228+
open class Accessible4 {
229+
open subscript(dynamicMember member: String) -> Int {
230+
return 42
231+
}
232+
}
233+
234+
@dynamicMemberLookup
235+
public struct Accessible5 {
236+
subscript(dynamicMember member: String) -> Int {
237+
return 42
238+
}
239+
240+
public subscript(dynamicMember member: StaticString) -> Int {
241+
return 42
242+
}
243+
}
244+
245+
@dynamicMemberLookup
246+
public struct Inaccessible1 {
247+
// expected-non-resilient-warning @+2 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type; this will be an error in a future Swift language mode}}{{3-3=public }}
248+
// expected-resilient-error @+1 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type}}{{3-3=public }}
249+
subscript(dynamicMember member: String) -> Int {
250+
return 42
251+
}
252+
}
253+
254+
@dynamicMemberLookup
255+
public struct Inaccessible2 {
256+
// expected-non-resilient-warning @+3 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type; this will be an error in a future Swift language mode}}{{21-29=public}}
257+
// expected-resilient-error @+2 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type}}{{21-29=public}}
258+
// expected-error @+1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but subscript 'subscript(dynamicMember:)' is public}}
259+
@usableFromInline internal subscript(dynamicMember member: String) -> Int {
260+
return 42
261+
}
262+
}
263+
264+
@dynamicMemberLookup
265+
internal struct Inaccessible3 {
266+
// expected-non-resilient-warning @+2 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type; this will be an error in a future Swift language mode}}{{3-10=internal}}
267+
// expected-resilient-error @+1 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type}}{{3-10=internal}}
268+
private subscript(dynamicMember member: String) -> Int {
269+
return 42
270+
}
271+
}
272+
273+
@dynamicMemberLookup
274+
private struct Inaccessible4 {
275+
// expected-non-resilient-warning @+2 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type; this will be an error in a future Swift language mode}}{{3-10=fileprivate}}
276+
// expected-resilient-error @+1 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type}}{{3-10=fileprivate}}
277+
private subscript(dynamicMember member: String) -> Int {
278+
return 42
279+
}
280+
}
281+
201282
//===----------------------------------------------------------------------===//
202283
// Existentials
203284
//===----------------------------------------------------------------------===//
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 7
2+
// REQUIRES: swift7
3+
4+
@dynamicMemberLookup
5+
public struct Accessible1 {
6+
public subscript(dynamicMember member: String) -> Int {
7+
return 42
8+
}
9+
}
10+
11+
@dynamicMemberLookup
12+
private struct Accessible2 {
13+
fileprivate subscript(dynamicMember member: String) -> Int {
14+
return 42
15+
}
16+
}
17+
18+
@dynamicMemberLookup
19+
open class Accessible3 {
20+
public subscript(dynamicMember member: String) -> Int {
21+
return 42
22+
}
23+
}
24+
25+
@dynamicMemberLookup
26+
open class Accessible4 {
27+
open subscript(dynamicMember member: String) -> Int {
28+
return 42
29+
}
30+
}
31+
32+
@dynamicMemberLookup
33+
public struct Accessible5 {
34+
subscript(dynamicMember member: String) -> Int {
35+
return 42
36+
}
37+
38+
public subscript(dynamicMember member: StaticString) -> Int {
39+
return 42
40+
}
41+
}
42+
43+
@dynamicMemberLookup
44+
public struct Inaccessible1 {
45+
// expected-error @+1 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type}}{{3-3=public }}
46+
subscript(dynamicMember member: String) -> Int {
47+
return 42
48+
}
49+
}
50+
51+
@dynamicMemberLookup
52+
public struct Inaccessible2 {
53+
// expected-error @+2 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type}}{{21-29=public}}
54+
// expected-error @+1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but subscript 'subscript(dynamicMember:)' is public}}
55+
@usableFromInline internal subscript(dynamicMember member: String) -> Int {
56+
return 42
57+
}
58+
}
59+
60+
@dynamicMemberLookup
61+
internal struct Inaccessible3 {
62+
// expected-error @+1 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type}}{{3-10=internal}}
63+
private subscript(dynamicMember member: String) -> Int {
64+
return 42
65+
}
66+
}
67+
68+
@dynamicMemberLookup
69+
private struct Inaccessible4 {
70+
// expected-error @+1 {{'@dynamicMemberLookup' requires 'subscript(dynamicMember:)' to be as accessible as its enclosing type}}{{3-10=fileprivate}}
71+
private subscript(dynamicMember member: String) -> Int {
72+
return 42
73+
}
74+
}

test/embedded/keypath-crash.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public struct Binding<Value> {
1212
self.wrappedValue = get()
1313
}
1414

15-
subscript<Subject>(dynamicMember keyPath: WritableKeyPath<Value, Subject>) -> Binding<Subject> {
15+
public subscript<Subject>(dynamicMember keyPath: WritableKeyPath<Value, Subject>) -> Binding<Subject> {
1616
get { fatalError() }
1717
}
1818
}

test/embedded/keypaths2.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ public struct Box<T>: ~Copyable {
1212
self.value = UnsafeMutablePointer<T>.allocate(capacity: 1)
1313
}
1414

15-
subscript<U>(dynamicMember member: WritableKeyPath<T, U>) -> U {
15+
public subscript<U>(dynamicMember member: WritableKeyPath<T, U>) -> U {
1616
@_transparent
1717
get { return self.value.pointer(to: member)!.pointee }
1818

1919
@_transparent
2020
set { self.value.pointer(to: member)!.pointee = newValue }
2121
}
2222

23+
@usableFromInline
2324
var value: UnsafeMutablePointer<T>
2425
}
2526

0 commit comments

Comments
 (0)