Skip to content

Commit 817a2c0

Browse files
committed
Add @_implicitSelfCapture attribute to disable "self." requirement.
Add a new parameter attribute `@_implicitSelfCapture` that disables the requirement to explicitly use `self.` to refer to a member of `self` in an escaping closure. Part of rdar://76927008. (cherry picked from commit abfc9bc)
1 parent 1bc1a10 commit 817a2c0

File tree

10 files changed

+93
-11
lines changed

10 files changed

+93
-11
lines changed

include/swift/AST/Attr.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,11 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(_unsafeMainActor, UnsafeMainActor,
647647
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove,
648648
114)
649649

650+
SIMPLE_DECL_ATTR(_implicitSelfCapture, ImplicitSelfCapture,
651+
OnParam | UserInaccessible |
652+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove,
653+
115)
654+
650655
#undef TYPE_ATTR
651656
#undef DECL_ATTR_ALIAS
652657
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/Expr.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,14 @@ class alignas(8) Expr {
292292
Kind : 2
293293
);
294294

295-
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1,
295+
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1,
296296
/// True if closure parameters were synthesized from anonymous closure
297297
/// variables.
298-
HasAnonymousClosureVars : 1
298+
HasAnonymousClosureVars : 1,
299+
300+
/// True if "self" can be captured implicitly without requiring "self."
301+
/// on each member reference.
302+
ImplicitSelfCapture : 1
299303
);
300304

301305
SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16,
@@ -3871,6 +3875,7 @@ class ClosureExpr : public AbstractClosureExpr {
38713875
Body(nullptr) {
38723876
setParameterList(params);
38733877
Bits.ClosureExpr.HasAnonymousClosureVars = false;
3878+
Bits.ClosureExpr.ImplicitSelfCapture = false;
38743879
}
38753880

38763881
SourceRange getSourceRange() const;
@@ -3898,7 +3903,17 @@ class ClosureExpr : public AbstractClosureExpr {
38983903
void setHasAnonymousClosureVars() {
38993904
Bits.ClosureExpr.HasAnonymousClosureVars = true;
39003905
}
3901-
3906+
3907+
/// Whether this closure allows "self" to be implicitly captured without
3908+
/// required "self." on each reference.
3909+
bool allowsImplicitSelfCapture() const {
3910+
return Bits.ClosureExpr.ImplicitSelfCapture;
3911+
}
3912+
3913+
void setAllowsImplicitSelfCapture(bool value = true) {
3914+
Bits.ClosureExpr.ImplicitSelfCapture = value;
3915+
}
3916+
39023917
/// Determine whether this closure expression has an
39033918
/// explicitly-specified result type.
39043919
bool hasExplicitResultType() const { return ArrowLoc.isValid(); }

include/swift/AST/Types.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3382,6 +3382,7 @@ struct ParameterListInfo {
33823382
SmallBitVector propertyWrappers;
33833383
SmallBitVector unsafeSendable;
33843384
SmallBitVector unsafeMainActor;
3385+
SmallBitVector implicitSelfCapture;
33853386

33863387
public:
33873388
ParameterListInfo() { }
@@ -3410,6 +3411,13 @@ struct ParameterListInfo {
34103411
/// part of the type system.
34113412
bool isUnsafeMainActor(unsigned paramIdx) const;
34123413

3414+
/// Whether the given parameter is a closure that should allow capture of
3415+
/// 'self' to be implicit, without requiring "self.".
3416+
bool isImplicitSelfCapture(unsigned paramIdx) const;
3417+
3418+
/// Whether there is any contextual information set on this parameter list.
3419+
bool anyContextualInfo() const;
3420+
34133421
/// Retrieve the number of non-defaulted parameters.
34143422
unsigned numNonDefaultedParameters() const {
34153423
return defaultArguments.count();

lib/AST/ASTDumper.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2546,6 +2546,8 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
25462546
printClosure(E, "closure_expr");
25472547
if (E->hasSingleExpressionBody())
25482548
PrintWithColorRAII(OS, ClosureModifierColor) << " single-expression";
2549+
if (E->allowsImplicitSelfCapture())
2550+
PrintWithColorRAII(OS, ClosureModifierColor) << " implicit-self";
25492551

25502552
if (E->getParameters()) {
25512553
OS << '\n';

lib/AST/Type.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ ParameterListInfo::ParameterListInfo(
970970
propertyWrappers.resize(params.size());
971971
unsafeSendable.resize(params.size());
972972
unsafeMainActor.resize(params.size());
973+
implicitSelfCapture.resize(params.size());
973974

974975
// No parameter owner means no parameter list means no default arguments
975976
// - hand back the zeroed bitvector.
@@ -1029,6 +1030,10 @@ ParameterListInfo::ParameterListInfo(
10291030
if (param->getAttrs().hasAttribute<UnsafeMainActorAttr>()) {
10301031
unsafeMainActor.set(i);
10311032
}
1033+
1034+
if (param->getAttrs().hasAttribute<ImplicitSelfCaptureAttr>()) {
1035+
implicitSelfCapture.set(i);
1036+
}
10321037
}
10331038
}
10341039

@@ -1059,6 +1064,17 @@ bool ParameterListInfo::isUnsafeMainActor(unsigned paramIdx) const {
10591064
: false;
10601065
}
10611066

1067+
bool ParameterListInfo::isImplicitSelfCapture(unsigned paramIdx) const {
1068+
return paramIdx < implicitSelfCapture.size()
1069+
? implicitSelfCapture[paramIdx]
1070+
: false;
1071+
}
1072+
1073+
bool ParameterListInfo::anyContextualInfo() const {
1074+
return unsafeSendable.any() || unsafeMainActor.any() ||
1075+
implicitSelfCapture.any();
1076+
}
1077+
10621078
/// Turn a param list into a symbolic and printable representation that does not
10631079
/// include the types, something like (_:, b:, c:)
10641080
std::string swift::getParamListAsString(ArrayRef<AnyFunctionType::Param> params) {

lib/Sema/CSApply.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5711,16 +5711,18 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
57115711
}
57125712

57135713
/// Apply the contextually Sendable flag to the given expression,
5714-
static void applyUnsafeConcurrent(
5715-
Expr *expr, bool sendable, bool forMainActor) {
5714+
static void applyContextualClosureFlags(
5715+
Expr *expr, bool sendable, bool forMainActor, bool implicitSelfCapture) {
57165716
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
57175717
closure->setUnsafeConcurrent(sendable, forMainActor);
5718+
closure->setAllowsImplicitSelfCapture(implicitSelfCapture);
57185719
return;
57195720
}
57205721

57215722
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
5722-
applyUnsafeConcurrent(
5723-
captureList->getClosureBody(), sendable, forMainActor);
5723+
applyContextualClosureFlags(
5724+
captureList->getClosureBody(), sendable, forMainActor,
5725+
implicitSelfCapture);
57245726
}
57255727
}
57265728

@@ -5808,7 +5810,8 @@ Expr *ExprRewriter::coerceCallArguments(
58085810

58095811
// Quickly test if any further fix-ups for the argument types are necessary.
58105812
if (AnyFunctionType::equalParams(args, params) &&
5811-
!shouldInjectWrappedValuePlaceholder)
5813+
!shouldInjectWrappedValuePlaceholder &&
5814+
!paramInfo.anyContextualInfo())
58125815
return arg;
58135816

58145817
// Apply labels to arguments.
@@ -5979,9 +5982,10 @@ Expr *ExprRewriter::coerceCallArguments(
59795982
bool isUnsafeSendable = paramInfo.isUnsafeSendable(paramIdx);
59805983
bool isMainActor = paramInfo.isUnsafeMainActor(paramIdx) ||
59815984
(isUnsafeSendable && apply && isMainDispatchQueue(apply->getFn()));
5982-
applyUnsafeConcurrent(
5985+
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
5986+
applyContextualClosureFlags(
59835987
arg, isUnsafeSendable && contextUsesConcurrencyFeatures(dc),
5984-
isMainActor);
5988+
isMainActor, isImplicitSelfCapture);
59855989

59865990
// If the types exactly match, this is easy.
59875991
auto paramType = param.getOldType();

lib/Sema/MiscDiagnostics.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,13 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
15701570
return false;
15711571
}
15721572

1573+
// If the closure was used in a context where it's explicitly stated
1574+
// that it does not need "self." qualification, don't require it.
1575+
if (auto closure = dyn_cast<ClosureExpr>(CE)) {
1576+
if (closure->allowsImplicitSelfCapture())
1577+
return false;
1578+
}
1579+
15731580
return true;
15741581
}
15751582

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
137137
IGNORED_ATTR(AtReasync)
138138
IGNORED_ATTR(UnsafeSendable)
139139
IGNORED_ATTR(UnsafeMainActor)
140+
IGNORED_ATTR(ImplicitSelfCapture)
140141
#undef IGNORED_ATTR
141142

142143
void visitAlignmentAttr(AlignmentAttr *attr) {

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1546,7 +1546,7 @@ namespace {
15461546
UNINTERESTING_ATTR(Nonisolated)
15471547
UNINTERESTING_ATTR(UnsafeSendable)
15481548
UNINTERESTING_ATTR(UnsafeMainActor)
1549-
1549+
UNINTERESTING_ATTR(ImplicitSelfCapture)
15501550
#undef UNINTERESTING_ATTR
15511551

15521552
void visitAvailableAttr(AvailableAttr *attr) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
func takeFn(@_implicitSelfCapture fn: @escaping () -> Int) { }
4+
func takeEscapingFn(fn: @escaping () -> Int) { }
5+
6+
class C {
7+
var property: Int = 0
8+
9+
func method() { }
10+
11+
func testMethod() {
12+
takeFn { // no errors
13+
method()
14+
return property
15+
}
16+
17+
takeEscapingFn { // expected-note 2 {{capture 'self' explicitly to enable implicit 'self' in this closure}}
18+
method() // expected-error{{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
19+
// expected-note@-1{{reference 'self.' explicitly}}
20+
return property // expected-error{{reference to property 'property' in closure requires explicit use of 'self' to make capture semantics explicit}}
21+
// expected-note@-1{{reference 'self.' explicitly}}
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)