Use AbstractStorageDecl::isSettableInSwift to prohibit direct writes to optional requirements

Stop pretending that an optional requirement is immutable via the `StorageImplInfo` request.
This approach has lead astray the conformance checker and may have had a negative impact
on other code paths, and it doesn't work for imported declarations because they bypass the
request. Instead, use a forwarding `AbstractStorageDecl::isSettableInSwift` method
that special-cases optional requirements.
This commit is contained in:
Anthony Latsis
2022-04-19 17:12:22 +03:00
parent 651af99d6e
commit 934964d49d
9 changed files with 143 additions and 66 deletions

View File

@@ -1,4 +1,6 @@
// RUN: %target-typecheck-verify-swift -disable-objc-attr-requires-foundation-module -enable-objc-interop
// RUN: %target-typecheck-verify-swift -disable-objc-attr-requires-foundation-module -import-objc-header %swift_src_root/test/Inputs/ObjCOptionalRequirements.h
// REQUIRES: objc_interop
@objc class Object {
var name: String
@@ -8,38 +10,49 @@
}
}
@objc protocol P {
@objc protocol SwiftProtocol {
@objc optional var object: Object { get set }
@objc optional subscript(_: Int) -> Object { get set }
@objc optional subscript(_: Bool) -> Object { get set }
}
func assertExactType<T>(of _: T, is _: T.Type) {}
// An optional storage component makes the key path read-only...
do {
let kp_property = \P.object
let kp_subscript = \P.[0]
let kp_property = \SwiftProtocol.object
let kp_subscript = \SwiftProtocol.[false]
var p: P
var p: SwiftProtocol
// expected-error@+1 {{cannot assign through subscript: 'kp_property' is a read-only key path}}
p[keyPath: kp_property] = Object(name: "nope")
// expected-error@+1 {{cannot assign through subscript: 'kp_subscript' is a read-only key path}}
p[keyPath: kp_subscript] = Object(name: "nope")
assertExactType(of: kp_property, is: KeyPath<P, Object?>.self)
assertExactType(of: kp_subscript, is: KeyPath<P, Object?>.self)
assertExactType(of: kp_property, is: KeyPath<SwiftProtocol, Object?>.self)
assertExactType(of: kp_subscript, is: KeyPath<SwiftProtocol, Object?>.self)
}
do {
let kp_property_objc = \ObjCProtocol.flag
var p: ObjCProtocol
// expected-error@+1 {{cannot assign through subscript: 'kp_property_objc' is a read-only key path}}
p[keyPath: kp_property_objc] = false
assertExactType(of: kp_property_objc, is: KeyPath<ObjCProtocol, Bool?>.self)
}
// ...unless a reference-writable component shows up later.
do {
let kp_propertyForce_name = \P.object!.name
let kp_subscriptForce_name = \P.[0]!.name
let kp_propertyForce_name = \SwiftProtocol.object!.name
let kp_subscriptForce_name = \SwiftProtocol.[true]!.name
let p: P
let p: SwiftProtocol
p[keyPath: kp_propertyForce_name] = "yes"
p[keyPath: kp_subscriptForce_name] = "yes"
assertExactType(of: kp_propertyForce_name, is: ReferenceWritableKeyPath<P, String>.self)
assertExactType(of: kp_subscriptForce_name, is: ReferenceWritableKeyPath<P, String>.self)
assertExactType(of: kp_propertyForce_name,
is: ReferenceWritableKeyPath<SwiftProtocol, String>.self)
assertExactType(of: kp_subscriptForce_name,
is: ReferenceWritableKeyPath<SwiftProtocol, String>.self)
}