Skip to content

Commit 3f66f26

Browse files
authored
Merge pull request #38488 from kavon/5.5-isolated-argument-label
[5.5] fix parsing of `isolated` as an argument label
2 parents 2bbedbe + c118271 commit 3f66f26

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
@@ -171,12 +171,15 @@ bool Parser::startsParameterName(bool isClosure) {
171171
return true;
172172

173173
// "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);
174+
// so look ahead one more token (two total) see if we have a ':' that would
175+
// indicate that this is an argument label.
176+
return lookahead<bool>(2, [&](CancellableBacktrackingScope &) {
177+
if (Tok.is(tok::colon))
178+
return true; // isolated :
179+
180+
// isolated x :
181+
return Tok.canBeArgumentLabel() && nextTok.is(tok::colon);
182+
});
180183
}
181184

182185
if (isOptionalToken(nextTok)
@@ -259,14 +262,30 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
259262
Tok.isContextualKeyword("__shared") ||
260263
Tok.isContextualKeyword("__owned") ||
261264
Tok.isContextualKeyword("isolated")) {
265+
262266
if (Tok.isContextualKeyword("isolated")) {
267+
// did we already find an 'isolated' type modifier?
263268
if (param.IsolatedLoc.isValid()) {
264269
diagnose(Tok, diag::parameter_specifier_repeated)
265-
.fixItRemove(Tok.getLoc());
270+
.fixItRemove(Tok.getLoc());
266271
consumeToken();
267-
} else {
268-
param.IsolatedLoc = consumeToken();
272+
continue;
269273
}
274+
275+
// is this 'isolated' token the identifier of an argument label?
276+
bool partOfArgumentLabel = lookahead<bool>(1, [&](CancellableBacktrackingScope &) {
277+
if (Tok.is(tok::colon))
278+
return true; // isolated :
279+
280+
// isolated x :
281+
return Tok.canBeArgumentLabel() && peekToken().is(tok::colon);
282+
});
283+
284+
if (partOfArgumentLabel)
285+
break;
286+
287+
// consume 'isolated' as type modifier
288+
param.IsolatedLoc = consumeToken();
270289
continue;
271290
}
272291

@@ -303,7 +322,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
303322
diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
304323
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
305324
}
306-
325+
307326
if (startsParameterName(isClosure)) {
308327
// identifier-or-none for the first name
309328
param.FirstNameLoc = consumeArgumentLabel(param.FirstName,
@@ -432,7 +451,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
432451
status.setIsParseError();
433452
}
434453
}
435-
454+
436455
// '...'?
437456
if (Tok.isEllipsis()) {
438457
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)