Skip to content

Commit f636e9a

Browse files
authored
Merge pull request #38422 from kavon/isolated-argument-label
2 parents 2c05750 + 5ffe8e0 commit f636e9a

File tree

3 files changed

+89
-11
lines changed

3 files changed

+89
-11
lines changed

include/swift/Parse/Parser.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,29 @@ class Parser {
556556
/// Return the next token that will be installed by \c consumeToken.
557557
const Token &peekToken();
558558

559+
/// Consumes K tokens within a backtracking scope before calling \c f and
560+
/// providing it with the backtracking scope. Unless if the backtracking is
561+
/// explicitly cancelled, the parser's token state is restored after \c f
562+
/// returns.
563+
///
564+
/// \param K The number of tokens ahead to skip. Zero is the current token.
565+
/// \param f The function to apply after skipping K tokens ahead.
566+
/// The value returned by \c f will be returned by \c peekToken
567+
/// after the parser is rolled back.
568+
/// \returns the value returned by \c f
569+
/// \note When calling, you may need to specify the \c Val type
570+
/// explicitly as a type parameter.
571+
template <typename Val>
572+
Val lookahead(unsigned char K,
573+
llvm::function_ref<Val(CancellableBacktrackingScope &)> f) {
574+
CancellableBacktrackingScope backtrackScope(*this);
575+
576+
for (unsigned char i = 0; i < K; ++i)
577+
consumeToken();
578+
579+
return f(backtrackScope);
580+
}
581+
559582
/// Consume a token that we created on the fly to correct the original token
560583
/// stream from lexer.
561584
void consumeExtraToken(Token K);

lib/Parse/ParsePattern.cpp

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,15 @@ bool Parser::startsParameterName(bool isClosure) {
172172
return true;
173173

174174
// "isolated" can be an argument label, but it's also a contextual keyword,
175-
// so look ahead one more token see if we have a ':' that would indicate
176-
// that this is an argument label.
177-
BacktrackingScope backtrackScope(*this);
178-
consumeToken();
179-
consumeToken();
180-
return Tok.is(tok::colon);
175+
// so look ahead one more token (two total) see if we have a ':' that would
176+
// indicate that this is an argument label.
177+
return lookahead<bool>(2, [&](CancellableBacktrackingScope &) {
178+
if (Tok.is(tok::colon))
179+
return true; // isolated :
180+
181+
// isolated x :
182+
return Tok.canBeArgumentLabel() && nextTok.is(tok::colon);
183+
});
181184
}
182185

183186
if (isOptionalToken(nextTok)
@@ -260,14 +263,30 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
260263
Tok.isContextualKeyword("__shared") ||
261264
Tok.isContextualKeyword("__owned") ||
262265
Tok.isContextualKeyword("isolated")) {
266+
263267
if (Tok.isContextualKeyword("isolated")) {
268+
// did we already find an 'isolated' type modifier?
264269
if (param.IsolatedLoc.isValid()) {
265270
diagnose(Tok, diag::parameter_specifier_repeated)
266-
.fixItRemove(Tok.getLoc());
271+
.fixItRemove(Tok.getLoc());
267272
consumeToken();
268-
} else {
269-
param.IsolatedLoc = consumeToken();
273+
continue;
270274
}
275+
276+
// is this 'isolated' token the identifier of an argument label?
277+
bool partOfArgumentLabel = lookahead<bool>(1, [&](CancellableBacktrackingScope &) {
278+
if (Tok.is(tok::colon))
279+
return true; // isolated :
280+
281+
// isolated x :
282+
return Tok.canBeArgumentLabel() && peekToken().is(tok::colon);
283+
});
284+
285+
if (partOfArgumentLabel)
286+
break;
287+
288+
// consume 'isolated' as type modifier
289+
param.IsolatedLoc = consumeToken();
271290
continue;
272291
}
273292

@@ -304,7 +323,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
304323
diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
305324
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
306325
}
307-
326+
308327
if (startsParameterName(isClosure)) {
309328
// identifier-or-none for the first name
310329
param.FirstNameLoc = consumeArgumentLabel(param.FirstName,
@@ -433,7 +452,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
433452
status.setIsParseError();
434453
}
435454
}
436-
455+
437456
// '...'?
438457
if (Tok.isEllipsis()) {
439458
Tok.setKind(tok::ellipsis);

test/Concurrency/isolated_parameters.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,39 @@ func testIsolatedParamCallsAsync(a: isolated A, b: A) async {
5454
// expected-note@-1{{calls to global function 'globalFuncIsolated' from outside of its actor context are implicitly asynchronous}}
5555
await globalFuncIsolated(b)
5656
}
57+
58+
actor MyActor {
59+
func hello() {}
60+
}
61+
62+
typealias MyFn = (isolated: Int) -> Void // expected-error {{function types cannot have argument labels; use '_' before 'isolated'}}
63+
typealias MyFnFixed = (_: isolated MyActor) -> Void
64+
65+
func standalone(_: isolated MyActor) {}
66+
func check() {
67+
let _: MyFnFixed = standalone
68+
let _: MyFnFixed = { (_: isolated MyActor) in () }
69+
}
70+
71+
72+
@available(SwiftStdlib 5.5, *)
73+
protocol P {
74+
func f(isolated: MyActor) async
75+
func g(isolated x: MyActor) async
76+
func h(isolated MyActor: isolated MyActor)
77+
func i(isolated: isolated MyActor)
78+
func j(isolated: Int) -> Int
79+
func k(isolated y: Int) -> Int
80+
func l(isolated _: Int) -> Int
81+
}
82+
83+
@available(SwiftStdlib 5.5, *)
84+
struct S: P {
85+
func f(isolated: MyActor) async { await isolated.hello() }
86+
func g(isolated x: MyActor) async { await x.hello() }
87+
func h(isolated MyActor: isolated MyActor) { i(isolated: MyActor) }
88+
func i(isolated: isolated MyActor) { isolated.hello() }
89+
func j(isolated: Int) -> Int { return isolated }
90+
func k(isolated y: Int) -> Int { return j(isolated: y) }
91+
func l(isolated _: Int) -> Int { return k(isolated: 0) }
92+
}

0 commit comments

Comments
 (0)