diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a964593605f15..2be6aba72d75e 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -721,6 +721,7 @@ bool BindingSet::finalize(bool transitive) { if (isValid && !capability) return false; + bool isContextualTypeReadOnly = false; // If the key path is sufficiently resolved we can add inferred binding // to the set. SmallSetVector updatedBindings; @@ -740,19 +741,30 @@ bool BindingSet::finalize(bool transitive) { } updatedBindings.insert(binding.withType(fnType)); + isContextualTypeReadOnly = true; + } else if (!(bindingTy->isWritableKeyPath() || + bindingTy->isReferenceWritableKeyPath())) { + isContextualTypeReadOnly = true; } } // Note that even though key path literal maybe be invalid it's // still the best course of action to use contextual function type // bindings because they allow to propagate type information from - // the key path into the context, so key path bindings are addded + // the key path into the context, so key path bindings are added // only if there is absolutely no other choice. if (updatedBindings.empty()) { auto rootTy = CS.getKeyPathRootType(keyPath); // A valid key path literal. if (capability) { + // Capability inference always results in a maximum mutability + // but if context is read-only it can be downgraded to avoid + // conversions. + if (isContextualTypeReadOnly) + capability = + std::make_pair(KeyPathMutability::ReadOnly, capability->second); + // Note that the binding is formed using root & value // type variables produced during constraint generation // because at this point root is already known (otherwise diff --git a/test/Concurrency/sendable_keypaths.swift b/test/Concurrency/sendable_keypaths.swift index b7081e8f2435c..e6700940c7f1e 100644 --- a/test/Concurrency/sendable_keypaths.swift +++ b/test/Concurrency/sendable_keypaths.swift @@ -61,7 +61,7 @@ do { let _: KeyPath = \.[NonSendable()] // ok let _: KeyPath & Sendable = \.[NonSendable()] // expected-warning {{type 'KeyPath' does not conform to the 'Sendable' protocol}} - let _: KeyPath & Sendable = \.[42, NonSendable(data: [-1, 0, 1])] // expected-warning {{type 'ReferenceWritableKeyPath' does not conform to the 'Sendable' protocol}} + let _: KeyPath & Sendable = \.[42, NonSendable(data: [-1, 0, 1])] // expected-warning {{type 'KeyPath' does not conform to the 'Sendable' protocol}} let _: KeyPath & Sendable = \.[42, -1] // Ok test(nonSendableKP) // expected-warning {{type 'KeyPath' does not conform to the 'Sendable' protocol}} @@ -106,7 +106,7 @@ do { // expected-warning@-1 {{converting non-Sendable function value to '@Sendable (V) -> Int' may introduce data races}} let _: KeyPath & Sendable = \.[42, CondSendable(NonSendable(data: [1, 2, 3]))] - // expected-warning@-1 {{type 'ReferenceWritableKeyPath' does not conform to the 'Sendable' protocol}} + // expected-warning@-1 {{type 'KeyPath' does not conform to the 'Sendable' protocol}} let _: KeyPath & Sendable = \.[42, CondSendable(42)] // Ok struct Root { @@ -114,14 +114,14 @@ do { } testSendableKP(v: v, \.[42, CondSendable(NonSendable(data: [1, 2, 3]))]) - // expected-warning@-1 {{type 'ReferenceWritableKeyPath' does not conform to the 'Sendable' protocol}} + // expected-warning@-1 {{type 'KeyPath' does not conform to the 'Sendable' protocol}} testSendableFn(v: v, \.[42, CondSendable(NonSendable(data: [1, 2, 3]))]) // expected-warning@-1 {{converting non-Sendable function value to '@Sendable (V) -> Int' may introduce data races}} testSendableKP(v: v, \.[42, CondSendable(42)]) // Ok let nonSendable = NonSendable() testSendableKP(v: v, \.[42, CondSendable(nonSendable)]) - // expected-warning@-1 {{type 'ReferenceWritableKeyPath' does not conform to the 'Sendable' protocol}} + // expected-warning@-1 {{type 'KeyPath' does not conform to the 'Sendable' protocol}} testSendableFn(v: v, \.[42, CondSendable(nonSendable)]) // expected-warning@-1 {{converting non-Sendable function value to '@Sendable (V) -> Int' may introduce data races}} @@ -277,3 +277,14 @@ do { // expected-warning@-1 {{type 'KeyPath' does not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} } } + +public final class TestSetterRef { + public internal(set) var v: Int = 0 // expected-note {{setter for property 'v' is not '@usableFromInline' or public}} + + public func test1(_ kp: KeyPath = \.v) {} // Ok + public func test2(_ kp: KeyPath = \TestSetterRef.v) {} // Ok + public func test3(_ kp: KeyPath & Sendable = \.v) {} // Ok + + public func test_err(_ kp: WritableKeyPath = \.v) {} + // expected-warning@-1 {{setter for property 'v' is internal and should not be referenced from a default argument value}} +} diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index 5ad49de915c75..dd22fa102abd1 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -738,11 +738,11 @@ class M { // CHECK-LABEL: // test_metatype_keypaths() // CHECK-LABEL: sil hidden [ossa] @{{.*}} : $@convention(thin) () -> () { func test_metatype_keypaths() { - // CHECK: keypath $ReferenceWritableKeyPath, (root $M.Type; settable_property $Int, id @$s8keypaths1MC10chanceRainSivgZ : $@convention(method) (@thick M.Type) -> Int, getter @$s8keypaths1MC10chanceRainSivpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Int, setter @$s8keypaths1MC10chanceRainSivpZACmTk : $@convention(keypath_accessor_setter) (@in_guaranteed Int, @in_guaranteed @thick M.Type) -> ()) + // CHECK: keypath $KeyPath, (root $M.Type; settable_property $Int, id @$s8keypaths1MC10chanceRainSivgZ : $@convention(method) (@thick M.Type) -> Int, getter @$s8keypaths1MC10chanceRainSivpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Int, setter @$s8keypaths1MC10chanceRainSivpZACmTk : $@convention(keypath_accessor_setter) (@in_guaranteed Int, @in_guaranteed @thick M.Type) -> ()) let _: KeyPath = \M.Type.chanceRain // CHECK: keypath $KeyPath, (root $M.Type; gettable_property $Bool, id @$s8keypaths1MC7isSunnySbvgZ : $@convention(method) (@thick M.Type) -> Bool, getter @$s8keypaths1MC7isSunnySbvpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Bool) let _: KeyPath = \M.Type.isSunny - // CHECK: keypath $ReferenceWritableKeyPath, (root $M.Type; settable_property $Bool, id @$s8keypaths1MC8isCloudySbvgZ : $@convention(method) (@thick M.Type) -> Bool, getter @$s8keypaths1MC8isCloudySbvpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Bool, setter @$s8keypaths1MC8isCloudySbvpZACmTk : $@convention(keypath_accessor_setter) (@in_guaranteed Bool, @in_guaranteed @thick M.Type) -> ()) + // CHECK: keypath $KeyPath, (root $M.Type; settable_property $Bool, id @$s8keypaths1MC8isCloudySbvgZ : $@convention(method) (@thick M.Type) -> Bool, getter @$s8keypaths1MC8isCloudySbvpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Bool, setter @$s8keypaths1MC8isCloudySbvpZACmTk : $@convention(keypath_accessor_setter) (@in_guaranteed Bool, @in_guaranteed @thick M.Type) -> ()) let _: KeyPath = \M.Type.isCloudy // CHECK: keypath $KeyPath, (root $M.Type; gettable_property $String, id @$s8keypaths1MCySSSicigZ : $@convention(method) (Int, @thick M.Type) -> @owned String, getter @$s8keypaths1MCySSSicipZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type, @in_guaranteed Int) -> @out String, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) (%{{.*}}) let _: KeyPath = \M.Type.[2]