From 56976c55d14e2ef87244c769af5bffadeeb293e4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 25 Jul 2025 12:14:22 -0700 Subject: [PATCH 1/2] [CSApply] Register argument matches for dynamic member lookup subscripts We missed this before because the argument/parameter types are equal. --- lib/Sema/CSApply.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 44c1025583730..82242ca451271 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3687,6 +3687,8 @@ namespace { if (!argExpr) return nullptr; + solution.recordSingleArgMatchingChoice(cs.getConstraintLocator(expr)); + // Build an argument list. auto *argList = ArgumentList::forImplicitSingle(ctx, ctx.Id_dynamicMember, argExpr); From db13a63588be2260333b911e8da50ce4ad60d8c7 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 25 Jul 2025 16:14:18 -0700 Subject: [PATCH 2/2] [CSApply] Key path dynamic member lookup argument is Sendable only if its captures are Previously dynamic member subscript wasn't allowed to use `& Sendable`, since this restriction was lifted the argument cannot simply assume the parameter type any longer, the key path captures have to be checked to determine whether it could be marked as Sendable or not. Resolves: https://github.com/swiftlang/swift/issues/77105 Resolves: rdar://138227393 --- lib/Sema/CSApply.cpp | 29 ++++++++++++++++++++---- test/Concurrency/sendable_keypaths.swift | 19 ++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 82242ca451271..bedf63acf1118 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2556,18 +2556,39 @@ namespace { /// Build an implicit argument for keypath based dynamic lookup, /// which consists of KeyPath expression and a single component. /// - /// \param argType The type of the keypath subscript argument. + /// \param paramType The type of the keypath subscript parameter + /// this argument is passed to. /// \param dotLoc The location of the '.' preceding member name. /// \param memberLoc The locator to be associated with new argument. - Expr *buildKeyPathDynamicMemberArgExpr(Type argType, SourceLoc dotLoc, + Expr *buildKeyPathDynamicMemberArgExpr(Type paramType, SourceLoc dotLoc, ConstraintLocator *memberLoc) { using Component = KeyPathExpr::Component; auto *anchor = getAsExpr(memberLoc->getAnchor()); auto makeKeyPath = [&](ArrayRef components) -> Expr * { + Type keyPathTy = paramType; + + // If parameter of a dynamic member lookup is `& Sendable` type + // we need to check key path captures to determine whether the + // argument could be `& Sendable` as well or not. + if (paramType->isExistentialType() && paramType->isSendableType()) { + auto allCapturesAreSendable = [&](const Component &component) { + auto *argList = component.getArgs(); + if (!argList) + return true; + + return llvm::all_of(*argList, [&](const auto &arg) { + return solution.getResolvedType(arg.getExpr())->isSendableType(); + }); + }; + + if (!llvm::all_of(components, allCapturesAreSendable)) + keyPathTy = paramType->getSuperclass(); + } + auto *kp = KeyPathExpr::createImplicit(ctx, /*backslashLoc*/ dotLoc, components, anchor->getEndLoc()); - kp->setType(argType); + kp->setType(keyPathTy); cs.cacheExprTypes(kp); // See whether there's an equivalent ObjC key path string we can produce @@ -2576,7 +2597,7 @@ namespace { return kp; }; - Type keyPathTy = argType; + Type keyPathTy = paramType; if (auto *existential = keyPathTy->getAs()) { keyPathTy = existential->getSuperclass(); assert(isKnownKeyPathType(keyPathTy)); diff --git a/test/Concurrency/sendable_keypaths.swift b/test/Concurrency/sendable_keypaths.swift index 66517cee7f46d..b4e4f7b2ef0ea 100644 --- a/test/Concurrency/sendable_keypaths.swift +++ b/test/Concurrency/sendable_keypaths.swift @@ -258,3 +258,22 @@ do { // TODO(rdar://125948508): This shouldn't be ambiguous (@Sendable version should be preferred) let _: () -> Void = forward(Test.fn) // expected-error {{conflicting arguments to generic parameter 'T' ('@Sendable () -> ()' vs. '() -> Void')}} } + +// https://github.com/swiftlang/swift/issues/77105 +do { + @dynamicMemberLookup + struct S { + subscript(dynamicMember keyPath: KeyPath & Sendable) -> U { + fatalError() + } + } + + struct Foo { + subscript(bar bar: T) -> Int { 42 } + } + + func test(s: S) { + _ = s[bar: NonSendable()] + // expected-warning@-1 {{type 'KeyPath' does not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} + } +}