Skip to content

Commit abfc9bc

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.
1 parent 8736683 commit abfc9bc

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
@@ -3381,6 +3381,7 @@ struct ParameterListInfo {
33813381
SmallBitVector propertyWrappers;
33823382
SmallBitVector unsafeSendable;
33833383
SmallBitVector unsafeMainActor;
3384+
SmallBitVector implicitSelfCapture;
33843385

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

3413+
/// Whether the given parameter is a closure that should allow capture of
3414+
/// 'self' to be implicit, without requiring "self.".
3415+
bool isImplicitSelfCapture(unsigned paramIdx) const;
3416+
3417+
/// Whether there is any contextual information set on this parameter list.
3418+
bool anyContextualInfo() const;
3419+
34123420
/// Retrieve the number of non-defaulted parameters.
34133421
unsigned numNonDefaultedParameters() const {
34143422
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
@@ -961,6 +961,7 @@ ParameterListInfo::ParameterListInfo(
961961
propertyWrappers.resize(params.size());
962962
unsafeSendable.resize(params.size());
963963
unsafeMainActor.resize(params.size());
964+
implicitSelfCapture.resize(params.size());
964965

965966
// No parameter owner means no parameter list means no default arguments
966967
// - hand back the zeroed bitvector.
@@ -1020,6 +1021,10 @@ ParameterListInfo::ParameterListInfo(
10201021
if (param->getAttrs().hasAttribute<UnsafeMainActorAttr>()) {
10211022
unsafeMainActor.set(i);
10221023
}
1024+
1025+
if (param->getAttrs().hasAttribute<ImplicitSelfCaptureAttr>()) {
1026+
implicitSelfCapture.set(i);
1027+
}
10231028
}
10241029
}
10251030

@@ -1050,6 +1055,17 @@ bool ParameterListInfo::isUnsafeMainActor(unsigned paramIdx) const {
10501055
: false;
10511056
}
10521057

1058+
bool ParameterListInfo::isImplicitSelfCapture(unsigned paramIdx) const {
1059+
return paramIdx < implicitSelfCapture.size()
1060+
? implicitSelfCapture[paramIdx]
1061+
: false;
1062+
}
1063+
1064+
bool ParameterListInfo::anyContextualInfo() const {
1065+
return unsafeSendable.any() || unsafeMainActor.any() ||
1066+
implicitSelfCapture.any();
1067+
}
1068+
10531069
/// Turn a param list into a symbolic and printable representation that does not
10541070
/// include the types, something like (_:, b:, c:)
10551071
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
@@ -5709,16 +5709,18 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
57095709
}
57105710

57115711
/// Apply the contextually Sendable flag to the given expression,
5712-
static void applyUnsafeConcurrent(
5713-
Expr *expr, bool sendable, bool forMainActor) {
5712+
static void applyContextualClosureFlags(
5713+
Expr *expr, bool sendable, bool forMainActor, bool implicitSelfCapture) {
57145714
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
57155715
closure->setUnsafeConcurrent(sendable, forMainActor);
5716+
closure->setAllowsImplicitSelfCapture(implicitSelfCapture);
57165717
return;
57175718
}
57185719

57195720
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
5720-
applyUnsafeConcurrent(
5721-
captureList->getClosureBody(), sendable, forMainActor);
5721+
applyContextualClosureFlags(
5722+
captureList->getClosureBody(), sendable, forMainActor,
5723+
implicitSelfCapture);
57225724
}
57235725
}
57245726

@@ -5803,7 +5805,8 @@ Expr *ExprRewriter::coerceCallArguments(
58035805

58045806
// Quickly test if any further fix-ups for the argument types are necessary.
58055807
if (AnyFunctionType::equalParams(args, params) &&
5806-
!shouldInjectWrappedValuePlaceholder)
5808+
!shouldInjectWrappedValuePlaceholder &&
5809+
!paramInfo.anyContextualInfo())
58075810
return arg;
58085811

58095812
// Apply labels to arguments.
@@ -5954,9 +5957,10 @@ Expr *ExprRewriter::coerceCallArguments(
59545957
bool isUnsafeSendable = paramInfo.isUnsafeSendable(paramIdx);
59555958
bool isMainActor = paramInfo.isUnsafeMainActor(paramIdx) ||
59565959
(isUnsafeSendable && apply && isMainDispatchQueue(apply->getFn()));
5957-
applyUnsafeConcurrent(
5960+
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
5961+
applyContextualClosureFlags(
59585962
arg, isUnsafeSendable && contextUsesConcurrencyFeatures(dc),
5959-
isMainActor);
5963+
isMainActor, isImplicitSelfCapture);
59605964

59615965
// If the types exactly match, this is easy.
59625966
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)