Skip to content

Commit 5004a54

Browse files
committed
[Concurrency] Make "self" parameter of actor-isolated functions 'isolated'.
The notion of "actor-isolated" currently exists at the declaration level. For functions, it is going to be captured in the function type itself, where 'self' is declared to be 'isolated'. Model isolation both ways: the 'self' of a method that is isolated to an actor instance will be 'isolated' as well. We are still using declaration-based checking of actor isolation. However, by mirroring this information we can move more incrementally over to doing checking based on 'isolated' parameters.
1 parent bd8626f commit 5004a54

23 files changed

+257
-118
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4526,9 +4526,6 @@ ERROR(nonisolated_local_var,none,
45264526
"'nonisolated' can not be applied to local variables",
45274527
())
45284528

4529-
ERROR(isolated_param_non_actor,none,
4530-
"'isolated' parameter cannot have not-actor type %0", (Type))
4531-
45324529
ERROR(actor_instance_property_wrapper,none,
45334530
"%0 property in property wrapper type %1 cannot be isolated to "
45344531
"the actor instance; consider 'nonisolated'",

include/swift/AST/TypeCheckRequests.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,20 @@ class ActorIsolationRequest :
10201020
bool isCached() const { return true; }
10211021
};
10221022

1023+
/// Determine whether the given function should have an isolated 'self'.
1024+
class HasIsolatedSelfRequest :
1025+
public SimpleRequest<HasIsolatedSelfRequest,
1026+
bool(ValueDecl *),
1027+
RequestFlags::Uncached> {
1028+
public:
1029+
using SimpleRequest::SimpleRequest;
1030+
1031+
private:
1032+
friend SimpleRequest;
1033+
1034+
bool evaluate(Evaluator &evaluator, ValueDecl *func) const;
1035+
};
1036+
10231037
/// Request whether the storage has a mutating getter.
10241038
class IsGetterMutatingRequest :
10251039
public SimpleRequest<IsGetterMutatingRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ SWIFT_REQUEST(TypeChecker, GlobalActorAttributeRequest,
114114
SWIFT_REQUEST(TypeChecker, ActorIsolationRequest,
115115
ActorIsolationState(ValueDecl *),
116116
Cached, NoLocationInfo)
117+
SWIFT_REQUEST(TypeChecker, HasIsolatedSelfRequest,
118+
bool(ValueDecl *),
119+
Uncached, NoLocationInfo)
117120
SWIFT_REQUEST(TypeChecker, FunctionOperatorRequest, OperatorDecl *(FuncDecl *),
118121
Cached, NoLocationInfo)
119122
SWIFT_REQUEST(NameLookup, GenericSignatureRequest,

include/swift/AST/Types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,6 +2866,9 @@ class AnyFunctionType : public TypeBase {
28662866
/// Whether the parameter is marked '@_nonEphemeral'
28672867
bool isNonEphemeral() const { return Flags.isNonEphemeral(); }
28682868

2869+
/// Whether the parameter is 'isolated'.
2870+
bool isIsolated() const { return Flags.isIsolated(); }
2871+
28692872
/// Whether the parameter is marked '@noDerivative'.
28702873
bool isNoDerivative() const { return Flags.isNoDerivative(); }
28712874

include/swift/Parse/Parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,9 @@ class Parser {
13701370
EnumElement,
13711371
};
13721372

1373+
/// Whether we are at the start of a parameter name when parsing a parameter.
1374+
bool startsParameterName(bool isClosure);
1375+
13731376
/// Parse a parameter-clause.
13741377
///
13751378
/// \verbatim

lib/AST/ASTContext.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2790,6 +2790,7 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD,
27902790
bool isStatic = false;
27912791
SelfAccessKind selfAccess = SelfAccessKind::NonMutating;
27922792
bool isDynamicSelf = false;
2793+
bool isIsolated = false;
27932794

27942795
if (auto *FD = dyn_cast<FuncDecl>(AFD)) {
27952796
isStatic = FD->isStatic();
@@ -2806,6 +2807,11 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD,
28062807
// FIXME: All methods of non-final classes should have this.
28072808
else if (wantDynamicSelf && FD->hasDynamicSelfResult())
28082809
isDynamicSelf = true;
2810+
2811+
// If this is a non-static method within an actor, the 'self' parameter
2812+
// is isolated if this declaration is isolated.
2813+
isIsolated = evaluateOrDefault(
2814+
Ctx.evaluator, HasIsolatedSelfRequest{AFD}, false);
28092815
} else if (auto *CD = dyn_cast<ConstructorDecl>(AFD)) {
28102816
if (isInitializingCtor) {
28112817
// initializing constructors of value types always have an implicitly
@@ -2839,7 +2845,7 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD,
28392845
if (isStatic)
28402846
return AnyFunctionType::Param(MetatypeType::get(selfTy, Ctx));
28412847

2842-
auto flags = ParameterTypeFlags();
2848+
auto flags = ParameterTypeFlags().withIsolated(isIsolated);
28432849
switch (selfAccess) {
28442850
case SelfAccessKind::Consuming:
28452851
flags = flags.withOwned(true);

lib/AST/ASTPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3234,6 +3234,9 @@ static void printParameterFlags(ASTPrinter &printer,
32343234
break;
32353235
}
32363236

3237+
if (flags.isIsolated())
3238+
printer.printKeyword("isolated", options, " ");
3239+
32373240
if (!options.excludeAttrKind(TAK_escaping) && escaping)
32383241
printer.printKeyword("@escaping", options, " ");
32393242
}

lib/AST/ASTVerifier.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,26 @@ class Verifier : public ASTWalker {
17581758
}
17591759
}
17601760

1761+
/// A version of AnyFunctionType::equalParams() that ignores "isolated"
1762+
/// parameters, which aren't represented in the type system.
1763+
static bool equalParamsIgnoringIsolation(
1764+
ArrayRef<AnyFunctionType::Param> a,
1765+
ArrayRef<AnyFunctionType::Param> b) {
1766+
auto withoutIsolation = [](AnyFunctionType::Param param) {
1767+
return param.withFlags(param.getParameterFlags().withIsolated(false));
1768+
};
1769+
1770+
if (a.size() != b.size())
1771+
return false;
1772+
1773+
for (unsigned i = 0, n = a.size(); i != n; ++i) {
1774+
if (withoutIsolation(a[i]) != withoutIsolation(b[i]))
1775+
return false;
1776+
}
1777+
1778+
return true;
1779+
}
1780+
17611781
void verifyChecked(ApplyExpr *E) {
17621782
PrettyStackTraceExpr debugStack(Ctx, "verifying ApplyExpr", E);
17631783

@@ -1782,7 +1802,7 @@ class Verifier : public ASTWalker {
17821802
Type InputExprTy = E->getArg()->getType();
17831803
AnyFunctionType::decomposeInput(InputExprTy, Args);
17841804
auto Params = FT->getParams();
1785-
if (!AnyFunctionType::equalParams(Args, Params)) {
1805+
if (!equalParamsIgnoringIsolation(Args, Params)) {
17861806
Out << "Argument type does not match parameter type in ApplyExpr:"
17871807
"\nArgument type: ";
17881808
InputExprTy.print(Out);

lib/Parse/ParsePattern.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,22 +150,37 @@ static ParserStatus parseDefaultArgument(
150150

151151
/// Determine whether we are at the start of a parameter name when
152152
/// parsing a parameter.
153-
static bool startsParameterName(Parser &parser, bool isClosure) {
153+
bool Parser::startsParameterName(bool isClosure) {
154154
// '_' cannot be a type, so it must be a parameter name.
155-
if (parser.Tok.is(tok::kw__))
155+
if (Tok.is(tok::kw__))
156156
return true;
157157

158158
// To have a parameter name here, we need a name.
159-
if (!parser.Tok.canBeArgumentLabel())
159+
if (!Tok.canBeArgumentLabel())
160160
return false;
161161

162-
// If the next token can be an argument label or is ':', this is a name.
163-
const auto &nextTok = parser.peekToken();
164-
if (nextTok.is(tok::colon) || nextTok.canBeArgumentLabel())
162+
// If the next token is ':', this is a name.
163+
const auto &nextTok = peekToken();
164+
if (nextTok.is(tok::colon))
165165
return true;
166166

167-
if (parser.isOptionalToken(nextTok)
168-
|| parser.isImplicitlyUnwrappedOptionalToken(nextTok))
167+
// If the next token can be an argument label, we might have a name.
168+
if (nextTok.canBeArgumentLabel()) {
169+
// If the first name wasn't "isolated", we're done.
170+
if (!Tok.isContextualKeyword("isolated"))
171+
return true;
172+
173+
// "isolated" can be an argument label, but it's also a contextual keyword,
174+
// so look ahead one more token see if we have a ':' that would indicate
175+
// that this is an argument label.
176+
BacktrackingScope backtrackScope(*this);
177+
consumeToken();
178+
consumeToken();
179+
return Tok.is(tok::colon);
180+
}
181+
182+
if (isOptionalToken(nextTok)
183+
|| isImplicitlyUnwrappedOptionalToken(nextTok))
169184
return false;
170185

171186
// The identifier could be a name or it could be a type. In a closure, we
@@ -289,7 +304,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
289304
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
290305
}
291306

292-
if (startsParameterName(*this, isClosure)) {
307+
if (startsParameterName(isClosure)) {
293308
// identifier-or-none for the first name
294309
param.FirstNameLoc = consumeArgumentLabel(param.FirstName,
295310
/*diagnoseDollarPrefix=*/!isClosure);

lib/Parse/ParseType.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,9 +1027,7 @@ ParserResult<TypeRepr> Parser::parseTypeTupleBody() {
10271027
// If the tuple element starts with a potential argument label followed by a
10281028
// ':' or another potential argument label, then the identifier is an
10291029
// element tag, and it is followed by a type annotation.
1030-
if (Tok.canBeArgumentLabel()
1031-
&& (peekToken().is(tok::colon)
1032-
|| peekToken().canBeArgumentLabel())) {
1030+
if (startsParameterName(false)) {
10331031
// Consume a name.
10341032
element.NameLoc = consumeArgumentLabel(element.Name,
10351033
/*diagnoseDollarPrefix=*/true);

0 commit comments

Comments
 (0)