@@ -3435,6 +3435,7 @@ class ObjCImplementationChecker {
34353435 WrongWritability,
34363436 WrongRequiredAttr,
34373437 WrongForeignErrorConvention,
3438+ WrongSendability,
34383439
34393440 Match,
34403441 MatchWithExplicitObjCName,
@@ -3621,70 +3622,93 @@ class ObjCImplementationChecker {
36213622 return lhs == rhs;
36223623 }
36233624
3624- static bool matchParamTypes (Type reqTy, Type implTy, ValueDecl *implDecl) {
3625+ static MatchOutcome matchParamTypes (Type reqTy, Type implTy,
3626+ ValueDecl *implDecl) {
36253627 TypeMatchOptions matchOpts = {};
3626-
36273628 // Try a plain type match.
36283629 if (implTy->matchesParameter (reqTy, matchOpts))
3629- return true ;
3630+ return MatchOutcome::Match ;
36303631
3631- // If the implementation type is IUO, try unwrapping it .
3632- if ( auto unwrappedImplTy = implTy-> getOptionalObjectType ())
3633- return implDecl-> isImplicitlyUnwrappedOptional ()
3634- && unwrappedImplTy-> matchesParameter (reqTy, matchOpts) ;
3632+ // Try to drop `@Sendable` .
3633+ {
3634+ auto ignoreSendable =
3635+ matchOpts | TypeMatchFlags::IgnoreFunctionSendability ;
36353636
3636- return false ;
3637+ if (implTy->matchesParameter (reqTy, ignoreSendable))
3638+ return MatchOutcome::WrongSendability;
3639+ }
3640+
3641+ if (implDecl) {
3642+ // If the implementation type is IUO, try unwrapping it.
3643+ if (auto unwrappedImplTy = implTy->getOptionalObjectType ())
3644+ return implDecl->isImplicitlyUnwrappedOptional () &&
3645+ unwrappedImplTy->matchesParameter (reqTy, matchOpts)
3646+ ? MatchOutcome::Match
3647+ : MatchOutcome::WrongType;
3648+ }
3649+
3650+ return MatchOutcome::WrongType;
36373651 }
36383652
3639- static bool matchTypes (Type reqTy, Type implTy, ValueDecl *implDecl) {
3653+ static MatchOutcome matchTypes (Type reqTy, Type implTy, ValueDecl *implDecl) {
36403654 TypeMatchOptions matchOpts = {};
36413655
36423656 // Try a plain type match.
36433657 if (reqTy->matches (implTy, matchOpts))
3644- return true ;
3658+ return MatchOutcome::Match ;
36453659
36463660 // If the implementation type is optional, try unwrapping it.
36473661 if (auto unwrappedImplTy = implTy->getOptionalObjectType ())
3648- return implDecl->isImplicitlyUnwrappedOptional ()
3649- && reqTy->matches (unwrappedImplTy, matchOpts);
3662+ return implDecl->isImplicitlyUnwrappedOptional () &&
3663+ reqTy->matches (unwrappedImplTy, matchOpts)
3664+ ? MatchOutcome::Match
3665+ : MatchOutcome::WrongType;
36503666
36513667 // Apply these rules to the result type and parameters if it's a function
36523668 // type.
3653- if (auto funcReqTy = reqTy->getAs <AnyFunctionType>())
3654- if (auto funcImplTy = implTy->getAs <AnyFunctionType>())
3655- return funcReqTy->matchesFunctionType (funcImplTy, matchOpts,
3656- [=]() -> bool {
3657- auto reqParams = funcReqTy->getParams ();
3658- auto implParams = funcImplTy->getParams ();
3659- if (reqParams.size () != implParams.size ())
3660- return false ;
3661-
3662- ParameterList *implParamList = nullptr ;
3663- if (auto afd = dyn_cast<AbstractFunctionDecl>(implDecl))
3664- implParamList = afd->getParameters ();
3665-
3666- for (auto i : indices (reqParams)) {
3667- const auto &reqParam = reqParams[i];
3668- const auto &implParam = implParams[i];
3669- if (implParamList) {
3670- // Some of the parameters may be IUOs; apply special logic.
3671- if (!matchParamTypes (reqParam.getOldType (),
3672- implParam.getOldType (),
3673- implParamList->get (i)))
3669+ if (auto funcReqTy = reqTy->getAs <AnyFunctionType>()) {
3670+ if (auto funcImplTy = implTy->getAs <AnyFunctionType>()) {
3671+ bool hasSendabilityMismatches = false ;
3672+ bool isMatch = funcReqTy->matchesFunctionType (
3673+ funcImplTy, matchOpts, [=, &hasSendabilityMismatches]() -> bool {
3674+ auto reqParams = funcReqTy->getParams ();
3675+ auto implParams = funcImplTy->getParams ();
3676+ if (reqParams.size () != implParams.size ())
36743677 return false ;
3675- } else {
3676- // IUOs not allowed here; apply ordinary logic.
3677- if (!reqParam.getOldType ()->matchesParameter (
3678- implParam.getOldType (), matchOpts))
3679- return false ;
3680- }
3681- }
36823678
3683- return matchTypes (funcReqTy-> getResult (), funcImplTy-> getResult (),
3684- implDecl);
3685- } );
3679+ ParameterList *implParamList = nullptr ;
3680+ if ( auto afd = dyn_cast<AbstractFunctionDecl>( implDecl))
3681+ implParamList = afd-> getParameters ( );
36863682
3687- return false ;
3683+ for (auto i : indices (reqParams)) {
3684+ const auto &reqParam = reqParams[i];
3685+ const auto &implParam = implParams[i];
3686+
3687+ auto outcome = matchParamTypes (
3688+ reqParam.getOldType (), implParam.getOldType (),
3689+ implParamList ? implParamList->get (i) : nullptr );
3690+
3691+ if (outcome == MatchOutcome::WrongSendability)
3692+ hasSendabilityMismatches = true ;
3693+
3694+ if (outcome < MatchOutcome::WrongSendability)
3695+ return false ;
3696+ }
3697+
3698+ return matchTypes (funcReqTy->getResult (), funcImplTy->getResult (),
3699+ implDecl) == MatchOutcome::Match;
3700+ });
3701+
3702+ if (isMatch) {
3703+ return hasSendabilityMismatches ? MatchOutcome::WrongSendability
3704+ : MatchOutcome::Match;
3705+ }
3706+
3707+ return MatchOutcome::WrongType;
3708+ }
3709+ }
3710+
3711+ return MatchOutcome::WrongType;
36883712 }
36893713
36903714 static Type getMemberType (ValueDecl *decl) {
@@ -3729,7 +3753,9 @@ class ObjCImplementationChecker {
37293753 if (cand->getKind () != req->getKind ())
37303754 return MatchOutcome::WrongDeclKind;
37313755
3732- if (!matchTypes (getMemberType (req), getMemberType (cand), cand))
3756+ auto matchTypesOutcome =
3757+ matchTypes (getMemberType (req), getMemberType (cand), cand);
3758+ if (matchTypesOutcome == MatchOutcome::WrongType)
37333759 return MatchOutcome::WrongType;
37343760
37353761 if (auto reqVar = dyn_cast<AbstractStorageDecl>(req))
@@ -3750,6 +3776,15 @@ class ObjCImplementationChecker {
37503776 if (explicitObjCName)
37513777 return MatchOutcome::MatchWithExplicitObjCName;
37523778
3779+ // Sendable mismatches are downgraded to warnings because ObjC
3780+ // declarations are `@preconcurrency`, we need to make sure
3781+ // that there are no other problems before returning `WrongSendability`.
3782+ if (matchTypesOutcome == MatchOutcome::WrongSendability)
3783+ return MatchOutcome::WrongSendability;
3784+
3785+ ASSERT (matchTypesOutcome == MatchOutcome::Match &&
3786+ " unexpected matchTypes() return" );
3787+
37533788 return MatchOutcome::Match;
37543789 }
37553790
@@ -3787,6 +3822,13 @@ class ObjCImplementationChecker {
37873822 diagnoseVTableUse (cand);
37883823 return ;
37893824
3825+ case MatchOutcome::WrongSendability:
3826+ diagnose (cand, diag::objc_implementation_sendability_mismatch, cand,
3827+ getMemberType (cand), getMemberType (req))
3828+ .limitBehaviorWithPreconcurrency (DiagnosticBehavior::Warning,
3829+ /* preconcurrency*/ true );
3830+ return ;
3831+
37903832 case MatchOutcome::WrongImplicitObjCName:
37913833 case MatchOutcome::WrongExplicitObjCName: {
37923834 auto diag = diagnose (cand, diag::objc_implementation_wrong_objc_name,
0 commit comments