Skip to content

Commit bb247d7

Browse files
committed
[TypeChecker] Fix @_dynamicReplacement to skip sendable annotations on ObjC declarations
Refactors `@_dynamicReplacement` attribute verification to consider only exact matches first, and if there are none, attempt to strip away sendability on ObjC declarations to make sure that any new `@Sendable` or `any Sendable` introduced to `@precocurrency` declarations don't break the overload selection.
1 parent 3032add commit bb247d7

3 files changed

+101
-39
lines changed

lib/Sema/TypeCheckAttr.cpp

+83-39
Original file line numberDiff line numberDiff line change
@@ -3873,62 +3873,106 @@ findSimilarFunction(DeclNameRef replacedFunctionName,
38733873
// Note: we might pass a constant attribute when typechecker is nullptr.
38743874
// Any modification to attr must be guarded by a null check on TC.
38753875
//
3876-
SmallVector<ValueDecl *, 4> results;
3877-
lookupReplacedDecl(replacedFunctionName, attr, base, results);
3876+
SmallVector<ValueDecl *, 4> lookupResults;
3877+
lookupReplacedDecl(replacedFunctionName, attr, base, lookupResults);
38783878

3879-
for (auto *result : results) {
3879+
SmallVector<AbstractFunctionDecl *, 4> candidates;
3880+
for (auto *result : lookupResults) {
38803881
// Protocol requirements are not replaceable.
38813882
if (isa<ProtocolDecl>(result->getDeclContext()))
38823883
continue;
38833884
// Check for static/instance mismatch.
38843885
if (result->isStatic() != base->isStatic())
38853886
continue;
38863887

3887-
auto resultTy = result->getInterfaceType();
3888-
auto replaceTy = base->getInterfaceType();
3889-
TypeMatchOptions matchMode = TypeMatchFlags::AllowABICompatible;
3890-
matchMode |= TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes;
3891-
if (resultTy->matches(replaceTy, matchMode)) {
3892-
if (forDynamicReplacement && !result->isDynamic()) {
3893-
if (Diags) {
3894-
Diags->diagnose(attr->getLocation(),
3895-
diag::dynamic_replacement_function_not_dynamic,
3896-
result->getName());
3897-
attr->setInvalid();
3898-
}
3899-
return nullptr;
3888+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(result))
3889+
candidates.push_back(AFD);
3890+
}
3891+
3892+
if (candidates.empty()) {
3893+
if (Diags) {
3894+
Diags->diagnose(attr->getLocation(),
3895+
forDynamicReplacement
3896+
? diag::dynamic_replacement_function_not_found
3897+
: diag::specialize_target_function_not_found,
3898+
replacedFunctionName);
3899+
}
3900+
3901+
attr->setInvalid();
3902+
return nullptr;
3903+
}
3904+
3905+
auto replaceTy = base->getInterfaceType();
3906+
3907+
// Filter based on the exact type match first.
3908+
SmallVector<AbstractFunctionDecl *> matches;
3909+
llvm::copy_if(candidates, std::back_inserter(matches),
3910+
[&replaceTy](AbstractFunctionDecl *F) {
3911+
auto resultTy = F->getInterfaceType();
3912+
TypeMatchOptions matchMode =
3913+
TypeMatchFlags::AllowABICompatible;
3914+
matchMode |=
3915+
TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes;
3916+
return resultTy->matches(replaceTy, matchMode);
3917+
});
3918+
3919+
// If there are no exact matches, strip sendability annotations
3920+
// from functions imported from Objective-C. This is a narrow
3921+
// fix for now but it could be extended to cover all `@preconcurrency`
3922+
// declarations.
3923+
if (matches.empty()) {
3924+
llvm::copy_if(candidates, std::back_inserter(matches),
3925+
[&replaceTy](AbstractFunctionDecl *F) {
3926+
if (!F->hasClangNode())
3927+
return false;
3928+
3929+
auto resultTy = F->getInterfaceType();
3930+
TypeMatchOptions matchMode =
3931+
TypeMatchFlags::AllowABICompatible;
3932+
matchMode |=
3933+
TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes;
3934+
matchMode |= TypeMatchFlags::IgnoreFunctionSendability;
3935+
matchMode |= TypeMatchFlags::IgnoreSendability;
3936+
return resultTy->matches(replaceTy, matchMode);
3937+
});
3938+
}
3939+
3940+
if (matches.size() == 1) {
3941+
auto result = matches.front();
3942+
if (forDynamicReplacement && !result->isDynamic()) {
3943+
if (Diags) {
3944+
Diags->diagnose(attr->getLocation(),
3945+
diag::dynamic_replacement_function_not_dynamic,
3946+
result->getName());
3947+
attr->setInvalid();
39003948
}
3901-
return cast<AbstractFunctionDecl>(result);
3949+
return nullptr;
39023950
}
3951+
3952+
return result;
39033953
}
39043954

3955+
attr->setInvalid();
3956+
39053957
if (!Diags)
39063958
return nullptr;
39073959

3908-
if (results.empty()) {
3909-
Diags->diagnose(attr->getLocation(),
3910-
forDynamicReplacement
3911-
? diag::dynamic_replacement_function_not_found
3912-
: diag::specialize_target_function_not_found,
3913-
replacedFunctionName);
3914-
} else {
3915-
Diags->diagnose(attr->getLocation(),
3916-
forDynamicReplacement
3917-
? diag::dynamic_replacement_function_of_type_not_found
3918-
: diag::specialize_target_function_of_type_not_found,
3919-
replacedFunctionName,
3920-
base->getInterfaceType()->getCanonicalType());
3960+
Diags->diagnose(attr->getLocation(),
3961+
forDynamicReplacement
3962+
? diag::dynamic_replacement_function_of_type_not_found
3963+
: diag::specialize_target_function_of_type_not_found,
3964+
replacedFunctionName,
3965+
base->getInterfaceType()->getCanonicalType());
39213966

3922-
for (auto *result : results) {
3923-
Diags->diagnose(SourceLoc(),
3924-
forDynamicReplacement
3925-
? diag::dynamic_replacement_found_function_of_type
3926-
: diag::specialize_found_function_of_type,
3927-
result->getName(),
3928-
result->getInterfaceType()->getCanonicalType());
3929-
}
3967+
for (auto *result : matches) {
3968+
Diags->diagnose(SourceLoc(),
3969+
forDynamicReplacement
3970+
? diag::dynamic_replacement_found_function_of_type
3971+
: diag::specialize_found_function_of_type,
3972+
result->getName(),
3973+
result->getInterfaceType()->getCanonicalType());
39303974
}
3931-
attr->setInvalid();
3975+
39323976
return nullptr;
39333977
}
39343978

test/Concurrency/sendable_objc_attr_in_type_context_swift5.swift

+9
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^blo
8282
+(void)sendDataWithCompletion: (void (^)(MyValue *)) completion;
8383
@end
8484

85+
@interface TestDR : NSObject
86+
-(void) testWithCompletion: (void (^)(void)) completion;
87+
@end
88+
8589
#pragma clang assume_nonnull end
8690

8791
//--- main.swift
@@ -203,3 +207,8 @@ extension DataHandler : CompletionWithoutSendable {
203207
// expected-warning@-1 {{sendability of function types in class method 'sendData(completion:)' does not match requirement in protocol 'CompletionWithoutSendable'}}
204208
// It should be possible to infer `T` from method that mismatches on @Sendable in Swift 5 mode
205209
}
210+
211+
extension TestDR {
212+
@_dynamicReplacement(for: test(completion:))
213+
func __replaceObjCFunc(_: @escaping () -> Void) {} // Ok
214+
}

test/Concurrency/sendable_objc_attr_in_type_context_swift6.swift

+9
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^blo
8282
+(void)sendDataWithCompletion: (void (^)(MyValue *)) completion;
8383
@end
8484

85+
@interface TestDR : NSObject
86+
-(void) testWithCompletion: (void (^)(void)) completion;
87+
@end
88+
8589
#pragma clang assume_nonnull end
8690

8791
//--- main.swift
@@ -210,3 +214,8 @@ extension DataHandler : CompletionWithoutSendable {
210214
// expected-error@-1 {{sendability of function types in class method 'sendData(completion:)' does not match requirement in protocol 'CompletionWithoutSendable'}}
211215
// It should be possible to infer `T` from method that mismatches on @Sendable in Swift 5 mode
212216
}
217+
218+
extension TestDR {
219+
@_dynamicReplacement(for: test(completion:))
220+
func __replaceObjCFunc(_: @escaping () -> Void) {} // Ok
221+
}

0 commit comments

Comments
 (0)