Skip to content

[5.5] fix parsing of isolated as an argument label #38488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,29 @@ class Parser {
/// Return the next token that will be installed by \c consumeToken.
const Token &peekToken();

/// Consumes K tokens within a backtracking scope before calling \c f and
/// providing it with the backtracking scope. Unless if the backtracking is
/// explicitly cancelled, the parser's token state is restored after \c f
/// returns.
///
/// \param K The number of tokens ahead to skip. Zero is the current token.
/// \param f The function to apply after skipping K tokens ahead.
/// The value returned by \c f will be returned by \c peekToken
/// after the parser is rolled back.
/// \returns the value returned by \c f
/// \note When calling, you may need to specify the \c Val type
/// explicitly as a type parameter.
template <typename Val>
Val lookahead(unsigned char K,
llvm::function_ref<Val(CancellableBacktrackingScope &)> f) {
CancellableBacktrackingScope backtrackScope(*this);

for (unsigned char i = 0; i < K; ++i)
consumeToken();

return f(backtrackScope);
}

/// Consume a token that we created on the fly to correct the original token
/// stream from lexer.
void consumeExtraToken(Token K);
Expand Down
41 changes: 30 additions & 11 deletions lib/Parse/ParsePattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,15 @@ bool Parser::startsParameterName(bool isClosure) {
return true;

// "isolated" can be an argument label, but it's also a contextual keyword,
// so look ahead one more token see if we have a ':' that would indicate
// that this is an argument label.
BacktrackingScope backtrackScope(*this);
consumeToken();
consumeToken();
return Tok.is(tok::colon);
// so look ahead one more token (two total) see if we have a ':' that would
// indicate that this is an argument label.
return lookahead<bool>(2, [&](CancellableBacktrackingScope &) {
if (Tok.is(tok::colon))
return true; // isolated :

// isolated x :
return Tok.canBeArgumentLabel() && nextTok.is(tok::colon);
});
}

if (isOptionalToken(nextTok)
Expand Down Expand Up @@ -259,14 +262,30 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
Tok.isContextualKeyword("__shared") ||
Tok.isContextualKeyword("__owned") ||
Tok.isContextualKeyword("isolated")) {

if (Tok.isContextualKeyword("isolated")) {
// did we already find an 'isolated' type modifier?
if (param.IsolatedLoc.isValid()) {
diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(Tok.getLoc());
.fixItRemove(Tok.getLoc());
consumeToken();
} else {
param.IsolatedLoc = consumeToken();
continue;
}

// is this 'isolated' token the identifier of an argument label?
bool partOfArgumentLabel = lookahead<bool>(1, [&](CancellableBacktrackingScope &) {
if (Tok.is(tok::colon))
return true; // isolated :

// isolated x :
return Tok.canBeArgumentLabel() && peekToken().is(tok::colon);
});

if (partOfArgumentLabel)
break;

// consume 'isolated' as type modifier
param.IsolatedLoc = consumeToken();
continue;
}

Expand Down Expand Up @@ -303,7 +322,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
}

if (startsParameterName(isClosure)) {
// identifier-or-none for the first name
param.FirstNameLoc = consumeArgumentLabel(param.FirstName,
Expand Down Expand Up @@ -432,7 +451,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
status.setIsParseError();
}
}

// '...'?
if (Tok.isEllipsis()) {
Tok.setKind(tok::ellipsis);
Expand Down
36 changes: 36 additions & 0 deletions test/Concurrency/isolated_parameters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,39 @@ func testIsolatedParamCallsAsync(a: isolated A, b: A) async {
// expected-note@-1{{calls to global function 'globalFuncIsolated' from outside of its actor context are implicitly asynchronous}}
await globalFuncIsolated(b)
}

actor MyActor {
func hello() {}
}

typealias MyFn = (isolated: Int) -> Void // expected-error {{function types cannot have argument labels; use '_' before 'isolated'}}
typealias MyFnFixed = (_: isolated MyActor) -> Void

func standalone(_: isolated MyActor) {}
func check() {
let _: MyFnFixed = standalone
let _: MyFnFixed = { (_: isolated MyActor) in () }
}


@available(SwiftStdlib 5.5, *)
protocol P {
func f(isolated: MyActor) async
func g(isolated x: MyActor) async
func h(isolated MyActor: isolated MyActor)
func i(isolated: isolated MyActor)
func j(isolated: Int) -> Int
func k(isolated y: Int) -> Int
func l(isolated _: Int) -> Int
}

@available(SwiftStdlib 5.5, *)
struct S: P {
func f(isolated: MyActor) async { await isolated.hello() }
func g(isolated x: MyActor) async { await x.hello() }
func h(isolated MyActor: isolated MyActor) { i(isolated: MyActor) }
func i(isolated: isolated MyActor) { isolated.hello() }
func j(isolated: Int) -> Int { return isolated }
func k(isolated y: Int) -> Int { return j(isolated: y) }
func l(isolated _: Int) -> Int { return k(isolated: 0) }
}