diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 0b8195cf3c7..82a8d767267 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -556,6 +556,29 @@ public: /// 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 + Val lookahead(unsigned char K, + llvm::function_ref 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); diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 93b0467c0a8..c1fd9f24a48 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -172,12 +172,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(2, [&](CancellableBacktrackingScope &) { + if (Tok.is(tok::colon)) + return true; // isolated : + + // isolated x : + return Tok.canBeArgumentLabel() && nextTok.is(tok::colon); + }); } if (isOptionalToken(nextTok) @@ -260,14 +263,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(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; } @@ -304,7 +323,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, @@ -433,7 +452,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, status.setIsParseError(); } } - + // '...'? if (Tok.isEllipsis()) { Tok.setKind(tok::ellipsis); diff --git a/test/Concurrency/isolated_parameters.swift b/test/Concurrency/isolated_parameters.swift index 182b3340618..6ae10ea0337 100644 --- a/test/Concurrency/isolated_parameters.swift +++ b/test/Concurrency/isolated_parameters.swift @@ -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) } +}