diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 44c1025583730..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)); @@ -3687,6 +3708,8 @@ namespace { if (!argExpr) return nullptr; + solution.recordSingleArgMatchingChoice(cs.getConstraintLocator(expr)); + // Build an argument list. auto *argList = ArgumentList::forImplicitSingle(ctx, ctx.Id_dynamicMember, argExpr); 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}} + } +}