[Concurrency] InferSendableFromCaptures: Rework check to delay lookup

The current implementation of the check accounts only for the overload
choices present in the initial lookup but for some situations, like
bridged or optional base types, `performMemberLookup` uses a secondary
lookup as well, results of which are ignored.

Let's fold the check into `addChoice` instead and set the the flag there
to make sure that all of the choices are considered.

Resolves: rdar://143586718
This commit is contained in:
Pavel Yaskevich
2025-07-21 10:33:46 -07:00
parent 2e8f74f011
commit 94e28b63d7
2 changed files with 61 additions and 52 deletions

View File

@@ -10278,6 +10278,55 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
// have already been excluded.
llvm::SmallPtrSet<ValueDecl *, 2> excludedDynamicMembers;
// Delay solving member constraint for unapplied methods
// where the base type has a conditional Sendable conformance
auto shouldDelayDueToPartiallyResolvedBaseType =
[&](Type baseTy, ValueDecl *decl,
FunctionRefInfo functionRefInfo) -> bool {
if (!Context.LangOpts.hasFeature(Feature::InferSendableFromCaptures))
return false;
if (!Context.getProtocol(KnownProtocolKind::Sendable))
return false;
auto shouldCheckSendabilityOfBase = [&]() {
if (!isa_and_nonnull<FuncDecl>(decl))
return Type();
if (!decl->isInstanceMember() &&
!decl->getDeclContext()->getSelfProtocolDecl())
return Type();
auto hasAppliedSelf =
decl->hasCurriedSelf() && doesMemberRefApplyCurriedSelf(baseTy, decl);
auto numApplies = getNumApplications(hasAppliedSelf, functionRefInfo);
if (numApplies >= decl->getNumCurryLevels())
return Type();
return decl->isInstanceMember() ? baseTy->getMetatypeInstanceType()
: baseTy;
};
Type baseTyToCheck = shouldCheckSendabilityOfBase();
if (!baseTyToCheck)
return false;
auto sendableProtocol = Context.getProtocol(KnownProtocolKind::Sendable);
auto baseConformance = lookupConformance(baseTyToCheck, sendableProtocol);
return llvm::any_of(baseConformance.getConditionalRequirements(),
[&](const auto &req) {
if (req.getKind() != RequirementKind::Conformance)
return false;
return (req.getFirstType()->hasTypeVariable() &&
(req.getProtocolDecl()->isSpecificProtocol(
KnownProtocolKind::Sendable) ||
req.getProtocolDecl()->isSpecificProtocol(
KnownProtocolKind::SendableMetatype)));
});
};
// Local function that adds the given declaration if it is a
// reasonable choice.
auto addChoice = [&](OverloadChoice candidate) {
@@ -10304,6 +10353,12 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
auto baseTy = candidate.getBaseType();
const auto baseObjTy = baseTy->getRValueType();
// If we need to delay, update the status but record the member.
if (shouldDelayDueToPartiallyResolvedBaseType(
baseObjTy, decl, candidate.getFunctionRefInfo())) {
result.OverallResult = MemberLookupResult::Unsolved;
}
bool hasInstanceMembers = false;
bool hasInstanceMethods = false;
bool hasStaticMembers = false;
@@ -10645,58 +10700,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
return OverloadChoice(baseTy, cand, functionRefInfo);
};
// Delay solving member constraint for unapplied methods
// where the base type has a conditional Sendable conformance
if (Context.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) {
auto shouldCheckSendabilityOfBase = [&]() {
if (!Context.getProtocol(KnownProtocolKind::Sendable))
return Type();
for (const auto &result : lookup) {
auto decl = result.getValueDecl();
if (!isa_and_nonnull<FuncDecl>(decl))
continue;
if (!decl->isInstanceMember() &&
!decl->getDeclContext()->getSelfProtocolDecl())
continue;
auto hasAppliedSelf = decl->hasCurriedSelf() &&
doesMemberRefApplyCurriedSelf(baseObjTy, decl);
auto numApplies = getNumApplications(hasAppliedSelf, functionRefInfo);
if (numApplies >= decl->getNumCurryLevels())
continue;
return decl->isInstanceMember()
? instanceTy
: MetatypeType::get(instanceTy);
}
return Type();
};
if (Type baseTyToCheck = shouldCheckSendabilityOfBase()) {
auto sendableProtocol = Context.getProtocol(KnownProtocolKind::Sendable);
auto baseConformance = lookupConformance(baseTyToCheck, sendableProtocol);
if (llvm::any_of(
baseConformance.getConditionalRequirements(),
[&](const auto &req) {
if (req.getKind() != RequirementKind::Conformance)
return false;
return (req.getFirstType()->hasTypeVariable() &&
(req.getProtocolDecl()->isSpecificProtocol(
KnownProtocolKind::Sendable) ||
req.getProtocolDecl()->isSpecificProtocol(
KnownProtocolKind::SendableMetatype)));
})) {
result.OverallResult = MemberLookupResult::Unsolved;
return result;
}
}
}
// Add all results from this lookup.
for (auto result : lookup)
addChoice(getOverloadChoice(result.getValueDecl(),