Skip to content

Commit 34ca702

Browse files
committed
[Frontend] Feature flag for InferredSendableMethods
1 parent 9e408af commit 34ca702

File tree

6 files changed

+71
-37
lines changed

6 files changed

+71
-37
lines changed

include/swift/Basic/Features.def

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,6 @@ EXPERIMENTAL_FEATURE(RawLayout, true)
243243
/// Enables the "embedded" swift mode (no runtime).
244244
EXPERIMENTAL_FEATURE(Embedded, true)
245245

246-
247246
/// Enables noncopyable generics
248247
EXPERIMENTAL_FEATURE(NoncopyableGenerics, false)
249248

@@ -253,12 +252,18 @@ EXPERIMENTAL_FEATURE(TypedThrows, true)
253252
/// Allow destructuring stored `let` bindings in structs.
254253
EXPERIMENTAL_FEATURE(StructLetDestructuring, true)
255254

255+
<<<<<<< HEAD
256256
/// Enable non-escapable type attributes and function attributes that support
257257
/// lifetime-dependent results.
258258
EXPERIMENTAL_FEATURE(NonEscapableTypes, false)
259259

260260
/// Enable the `@extern` attribute.
261261
EXPERIMENTAL_FEATURE(Extern, true)
262+
=======
263+
// Infer Sendability of unapplied and partial applied methods
264+
// based on type. Global functions are always implicitly Sendable
265+
EXPERIMENTAL_FEATURE(InferSendableMethods, false)
266+
>>>>>>> 175f54caad6 ([Frontend] Feature flag for InferredSendableMethods)
262267

263268
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
264269
#undef EXPERIMENTAL_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3579,6 +3579,8 @@ static bool usesFeatureIsolatedDefaultValues(Decl *decl) {
35793579
return false;
35803580
}
35813581

3582+
static bool usesFeatureInferSendableMethods(Decl *decl) { return false; }
3583+
35823584
static bool usesFeaturePlaygroundExtendedCallbacks(Decl *decl) {
35833585
return false;
35843586
}

lib/Sema/CSSimplify.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9494,6 +9494,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
94949494
Type baseObjTy = baseTy->getRValueType();
94959495
Type instanceTy = baseObjTy;
94969496

9497+
auto &ctx = getASTContext();
94979498
auto memberNode = simplifyLocatorToAnchor(memberLocator);
94989499
auto memberLoc = memberNode ? memberNode.getStartLoc() : SourceLoc();
94999500

@@ -9521,13 +9522,20 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
95219522

95229523
// Delay solving member constraint for unapplied methods
95239524
// where the base type has a conditional Sendable conformance
9524-
if (functionRefKind == FunctionRefKind::Unapplied) {
9525-
auto sendableProtocol = DC->getParentModule()->getASTContext().getProtocol(KnownProtocolKind::Sendable);
9526-
auto baseSendable = swift::TypeChecker::conformsToProtocol(instanceTy, sendableProtocol, DC->getParentModule());
9527-
9528-
if (!baseSendable.isInvalid() && !baseSendable.getConditionalRequirements().empty() && instanceTy->hasTypeVariable()) {
9529-
result.OverallResult = MemberLookupResult::Unsolved;
9530-
return result;
9525+
if (ctx.LangOpts.hasFeature(Feature::InferSendableMethods)) {
9526+
if (functionRefKind == FunctionRefKind::Unapplied) {
9527+
auto sendableProtocol =
9528+
DC->getParentModule()->getASTContext().getProtocol(
9529+
KnownProtocolKind::Sendable);
9530+
auto baseSendable = swift::TypeChecker::conformsToProtocol(
9531+
instanceTy, sendableProtocol, DC->getParentModule());
9532+
9533+
if (!baseSendable.isInvalid() &&
9534+
!baseSendable.getConditionalRequirements().empty() &&
9535+
instanceTy->hasTypeVariable()) {
9536+
result.OverallResult = MemberLookupResult::Unsolved;
9537+
return result;
9538+
}
95319539
}
95329540
}
95339541

@@ -9553,7 +9561,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
95539561

95549562
// If the base type is a tuple type, look for the named or indexed member
95559563
// of the tuple.
9556-
auto &ctx = getASTContext();
95579564
if (auto baseTuple = baseObjTy->getAs<TupleType>()) {
95589565
if (!memberName.isSpecial()) {
95599566
StringRef nameStr = memberName.getBaseIdentifier().str();
@@ -10531,8 +10538,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1053110538
// If requested, generate a constraint.
1053210539
if (flags.contains(TMF_GenerateConstraints)) {
1053310540
auto *memberRef = Constraint::createMemberOrOuterDisjunction(
10534-
*this, kind, baseTy, memberTy, member, useDC, functionRefKind,
10535-
outerAlternatives, locator);
10541+
*this, kind, baseTy, memberTy, member, useDC, functionRefKind,
10542+
outerAlternatives, locator);
1053610543

1053710544
addUnsolvedConstraint(memberRef);
1053810545

@@ -10587,7 +10594,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1058710594
// It could either be a hole associated directly with the base
1058810595
// or a hole which came from result type of the chain.
1058910596
if (originatorLoc->isLastElement<
10590-
LocatorPathElt::UnresolvedMemberChainResult>()) {
10597+
LocatorPathElt::UnresolvedMemberChainResult>()) {
1059110598
auto *UMCR = castToExpr<UnresolvedMemberChainResultExpr>(
1059210599
originatorLoc->getAnchor());
1059310600
return UMCR->getChainBase() == getAsExpr(locator->getAnchor());

lib/Sema/ConstraintSystem.cpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2623,31 +2623,46 @@ ConstraintSystem::getTypeOfMemberReference(
26232623
auto *functionType = fullFunctionType->getResult()->getAs<FunctionType>();
26242624
functionType = unwrapPropertyWrapperParameterTypes(*this, funcDecl, functionRefKind,
26252625
functionType, locator);
2626-
auto sendableProtocol = useDC->getParentModule()->getASTContext().getProtocol(KnownProtocolKind::Sendable);
2627-
auto baseSendable = TypeChecker::conformsToProtocol( baseOpenedTy, sendableProtocol, useDC->getParentModule());
2628-
2629-
if (isSendableType(useDC->getParentModule(), baseOpenedTy)) {
2630-
if (baseSendable.getConditionalRequirements().empty())
2631-
//Functions w/o conditional conformances should be marked @Sendable
2632-
functionType = functionType->withExtInfo(functionType->getExtInfo().withConcurrent())->getAs<FunctionType>();
2633-
2634-
// Handle Conditional Conformances
2635-
for (auto req : baseSendable.getConditionalRequirements()){
2636-
if(!TypeChecker::conformsToProtocol( req.getFirstType(), sendableProtocol, useDC->getParentModule()).isInvalid()){
2626+
auto &ctx = DC->getASTContext();
2627+
auto *parentModule = useDC->getParentModule();
2628+
bool baseSendable = isSendableType(parentModule, baseOpenedTy);
2629+
bool inferredSendable =
2630+
ctx.LangOpts.hasFeature(Feature::InferSendableMethods);
2631+
2632+
if (inferredSendable) {
2633+
auto sendableProtocol = parentModule->getASTContext().getProtocol(
2634+
KnownProtocolKind::Sendable);
2635+
auto baseConformance = TypeChecker::conformsToProtocol(
2636+
baseOpenedTy, sendableProtocol, parentModule);
2637+
2638+
if (baseSendable) {
2639+
// Add @Sendable to functions without conditional conformances
2640+
if (baseConformance.getConditionalRequirements().empty())
26372641
functionType = functionType->withExtInfo(functionType->getExtInfo().withConcurrent())->getAs<FunctionType>();
2642+
2643+
// Handle Conditional Conformances
2644+
for (auto req : baseConformance.getConditionalRequirements()) {
2645+
if (!TypeChecker::conformsToProtocol(req.getFirstType(),
2646+
sendableProtocol, parentModule)
2647+
.isInvalid())
2648+
functionType =
2649+
functionType
2650+
->withExtInfo(functionType->getExtInfo().withConcurrent())
2651+
->getAs<FunctionType>();
26382652
}
26392653
}
26402654
}
2641-
2655+
26422656
// FIXME: Verify ExtInfo state is correct, not working by accident.
26432657
FunctionType::ExtInfo info;
26442658
openedType =
26452659
FunctionType::get(fullFunctionType->getParams(), functionType, info);
26462660

2647-
if (isSendableType(useDC->getParentModule(), baseOpenedTy)) {
2648-
// If this is actually a Sendable type, implicitly mark @Sendable
2661+
// Add @Sendable to openedType if possible
2662+
if (inferredSendable && baseSendable) {
26492663
auto origFnType = openedType->castTo<FunctionType>();
2650-
openedType = origFnType->withExtInfo(origFnType->getExtInfo().withConcurrent());
2664+
openedType =
2665+
origFnType->withExtInfo(origFnType->getExtInfo().withConcurrent());
26512666
}
26522667
}
26532668

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5481,12 +5481,19 @@ AnyFunctionType *swift::adjustFunctionTypeForConcurrency(
54815481
bool strictChecking = contextRequiresStrictConcurrencyChecking(
54825482
dc, getType, isolatedByPreconcurrency);
54835483

5484-
auto func = dyn_cast_or_null<AbstractFunctionDecl>(decl);
5485-
auto doesNotCapture = func->getCaptureInfo().getCaptures().empty();
5484+
auto &ctx = dc->getASTContext();
5485+
bool inferredSendable =
5486+
ctx.LangOpts.hasFeature(Feature::InferSendableMethods);
54865487

5487-
if (doesNotCapture) {
5488-
fnType = fnType->withExtInfo(fnType->getExtInfo().withConcurrent());
5489-
return fnType;
5488+
if (inferredSendable) {
5489+
if (auto func = dyn_cast_or_null<AbstractFunctionDecl>(decl)) {
5490+
auto doesNotCapture = func->getCaptureInfo().getCaptures().empty();
5491+
5492+
if (doesNotCapture) {
5493+
fnType = fnType->withExtInfo(fnType->getExtInfo().withConcurrent());
5494+
return fnType;
5495+
}
5496+
}
54905497
}
54915498

54925499
fnType = applyUnsafeConcurrencyToFunctionType(

test/Concurrency/sendable_methods.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift-enable -experimental-feature InferSendableMethods
22
// REQUIRES: concurrency
33

44
final class C : Sendable {
@@ -77,10 +77,8 @@ g(InferredSendableE.f)
7777
g(GenericS<Int>.f)
7878
g(GenericC<Int>.f)
7979

80-
g(GenericS<NonSendable>.f) // expected-warning{{converting non-sendable function value to '@Sendable (GenericS<NonSendable>) -> (@Sendable () -> Void)' may introduce data races
81-
// expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races
82-
g(GenericC<NonSendable>.f) // expected-warning{{converting non-sendable function value to '@Sendable (GenericS<NonSendable>) -> (@Sendable () -> Void)' may introduce data races
83-
// expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races
80+
g(GenericS<NonSendable>.f) // expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races
81+
g(GenericC<NonSendable>.f) // expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races
8482

8583
func executeAsTask (_ f: @escaping @Sendable () -> Void) {
8684
Task {

0 commit comments

Comments
 (0)