Skip to content

Add support for @lifetime(borrow ...) #2894

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 2 commits into from
Nov 9, 2024
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
2 changes: 1 addition & 1 deletion CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public let EXPR_NODES: [Node] = [
children: [
Child(
name: "borrowKeyword",
kind: .token(choices: [.keyword(._borrow)])
kind: .token(choices: [.keyword(._borrow), .keyword(.borrow)])
),
Child(
name: "expression",
Expand Down
3 changes: 3 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ public enum Keyword: CaseIterable {
case backDeployed
case before
case block
case borrow
case borrowing
case `break`
case canImport
Expand Down Expand Up @@ -442,6 +443,8 @@ public enum Keyword: CaseIterable {
return KeywordSpec("before")
case .block:
return KeywordSpec("block")
case .borrow:
return KeywordSpec("borrow")
case .borrowing:
return KeywordSpec("borrowing")
case .break:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,6 @@ class ValidateSyntaxNodes: XCTestCase {
),

// MARK: Tokens that contain underscores
ValidationFailure(
node: .borrowExpr,
message:
"child 'borrowKeyword' has a single keyword as its only token choice and should thus be named '_borrowKeyword'"
// _borrow is underscored and thus BorrowKeyword is the correct spelling
),
ValidationFailure(
node: .conventionWitnessMethodAttributeArguments,
message:
Expand Down Expand Up @@ -410,7 +404,11 @@ class ValidateSyntaxNodes: XCTestCase {
message: "child 'consumeKeyword' only has keywords as its token choices and should thus end with 'Specifier'"
// ConsumeKeyword can be 'consume' or '_move' and '_move' is deprecated
),

ValidationFailure(
node: .borrowExpr,
message: "child 'borrowKeyword' only has keywords as its token choices and should thus end with 'Specifier'"
// BorrowKeyword can be 'borrow' or '_borrow', eventually '_borrow' will be deprecated
),
// MARK: Conceptually a value, not a specifier
ValidationFailure(
node: .booleanLiteralExpr,
Expand Down
5 changes: 5 additions & 0 deletions Sources/SwiftParser/Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,11 @@ extension Parser {
)
)
case (._borrow, let handle)?:
fallthrough
case (.borrow, let handle)?:
if !atContextualExpressionModifier() {
break EXPR_PREFIX
}
let borrowTok = self.eat(handle)
let sub = self.parseSequenceExpressionElement(
flavor: flavor,
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftParser/TokenPrecedence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ enum TokenPrecedence: Comparable {
.__consuming, .final, .required, .optional, .lazy, .dynamic, .infix, .postfix, .prefix, .mutating, .nonmutating,
.convenience, .override, .package, .open,
.__setter_access, .indirect, .isolated, .nonisolated, .distributed, ._local,
.inout, ._mutating, ._borrow, ._borrowing, .borrowing, ._consuming, .consuming, .consume,
.inout, ._mutating, ._borrow, ._borrowing, .borrow, .borrowing, ._consuming, .consuming, .consume,
.dependsOn, .scoped, .sending,
// Accessors
.get, .set, .didSet, .willSet, .unsafeAddress, .addressWithOwner, .addressWithNativeOwner, .unsafeMutableAddress,
Expand Down
3 changes: 3 additions & 0 deletions Sources/SwiftParser/TokenSpecSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@ enum ExpressionModifierKeyword: TokenSpecSet {
case _move
case _borrow
case `try`
case borrow
case consume
case copy
case `repeat`
Expand All @@ -706,6 +707,7 @@ enum ExpressionModifierKeyword: TokenSpecSet {
case TokenSpec(._move): self = ._move
case TokenSpec(._borrow): self = ._borrow
case TokenSpec(.try): self = .try
case TokenSpec(.borrow): self = .borrow
case TokenSpec(.consume): self = .consume
case TokenSpec(.copy): self = .copy
case TokenSpec(.repeat): self = .repeat
Expand All @@ -720,6 +722,7 @@ enum ExpressionModifierKeyword: TokenSpecSet {
case .await: return .keyword(.await)
case ._move: return .keyword(._move)
case ._borrow: return .keyword(._borrow)
case .borrow: return .keyword(.borrow)
case .consume: return .keyword(.consume)
case .copy: return .keyword(.copy)
case .try: return .keyword(.try)
Expand Down
52 changes: 52 additions & 0 deletions Sources/SwiftParser/generated/Parser+TokenSpecSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,58 @@ extension BooleanLiteralExprSyntax {
}
}

extension BorrowExprSyntax {
@_spi(Diagnostics)
public enum BorrowKeywordOptions: TokenSpecSet {
case _borrow
case borrow

init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) {
switch PrepareForKeywordMatch(lexeme) {
case TokenSpec(._borrow):
self = ._borrow
case TokenSpec(.borrow):
self = .borrow
default:
return nil
}
}

public init?(token: TokenSyntax) {
switch token {
case TokenSpec(._borrow):
self = ._borrow
case TokenSpec(.borrow):
self = .borrow
default:
return nil
}
}

var spec: TokenSpec {
switch self {
case ._borrow:
return .keyword(._borrow)
case .borrow:
return .keyword(.borrow)
}
}

/// Returns a token that satisfies the `TokenSpec` of this case.
///
/// If the token kind of this spec has variable text, e.g. for an identifier, this returns a token with empty text.
@_spi(Diagnostics)
public var tokenSyntax: TokenSyntax {
switch self {
case ._borrow:
return .keyword(._borrow)
case .borrow:
return .keyword(.borrow)
}
}
}
}

extension _CanImportVersionInfoSyntax {
@_spi(Diagnostics)
public enum LabelOptions: TokenSpecSet {
Expand Down
4 changes: 4 additions & 0 deletions Sources/SwiftSyntax/generated/Keyword.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public enum Keyword: UInt8, Hashable, Sendable {
case backDeployed
case before
case block
case borrow
case borrowing
case `break`
case canImport
Expand Down Expand Up @@ -416,6 +417,8 @@ public enum Keyword: UInt8, Hashable, Sendable {
self = ._local
case "before":
self = .before
case "borrow":
self = .borrow
case "deinit":
self = .deinit
case "didSet":
Expand Down Expand Up @@ -890,6 +893,7 @@ public enum Keyword: UInt8, Hashable, Sendable {
"backDeployed",
"before",
"block",
"borrow",
"borrowing",
"break",
"canImport",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
case .borrowExpr:
assert(layout.count == 5)
assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.keyword("_borrow")]))
assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.keyword("_borrow"), .keyword("borrow")]))
assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 3, verify(layout[3], as: RawExprSyntax.self))
assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4497,7 +4497,7 @@ public struct BooleanLiteralExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea

/// ### Children
///
/// - `borrowKeyword`: `_borrow`
/// - `borrowKeyword`: (`_borrow` | `borrow`)
/// - `expression`: ``ExprSyntax``
public struct BorrowExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyntaxNodeProtocol {
public let _syntaxNode: Syntax
Expand All @@ -4515,7 +4515,7 @@ public struct BorrowExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyn
public init(
leadingTrivia: Trivia? = nil,
_ unexpectedBeforeBorrowKeyword: UnexpectedNodesSyntax? = nil,
borrowKeyword: TokenSyntax = .keyword(._borrow),
borrowKeyword: TokenSyntax,
_ unexpectedBetweenBorrowKeywordAndExpression: UnexpectedNodesSyntax? = nil,
expression: some ExprSyntaxProtocol,
_ unexpectedAfterExpression: UnexpectedNodesSyntax? = nil,
Expand Down Expand Up @@ -4559,7 +4559,9 @@ public struct BorrowExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyn

/// ### Tokens
///
/// For syntax trees generated by the parser, this is guaranteed to be `_borrow`.
/// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds:
/// - `_borrow`
/// - `borrow`
public var borrowKeyword: TokenSyntax {
get {
return Syntax(self).child(at: 1)!.cast(TokenSyntax.self)
Expand Down
26 changes: 26 additions & 0 deletions Tests/SwiftParserTest/AttributeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1053,4 +1053,30 @@ final class AttributeTests: ParserTestCase {
"""
)
}

func testLifetimeAttribute() {
assertParse(
"""
struct NE: ~Escapable {}

@lifetime(ne)
func derive1(ne: NE) -> NE { ne }

@lifetime(borrow ne)
func derive2(ne: borrowing NE) -> NE { ne }

@lifetime(ne1, n2)
func derive3(ne1: NE, ne2: NE) -> NE { ne1 }

@lifetime(borrow ne1, n2)
func derive4(ne1: NE, ne2: NE) -> NE { ne1 }

@lifetime(neOut: ne)
func derive5(ne: NE, neOut: inout NE) -> NE { neOut = ne }

@lifetime(neOut: borrow ne)
func derive6(ne: borrowing NE, neOut: inout NE) -> NE { neOut = ne }
"""
)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add a test case for borrow blah in a normal expression position and one that ensure we parse borrow(blah), borrow (blah) as function calls?

}
32 changes: 32 additions & 0 deletions Tests/SwiftParserTest/ExpressionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,38 @@ final class ExpressionTests: ParserTestCase {
assertParse("use(_borrow msg)")
assertParse("_borrow msg")
assertParse("let b = (_borrow self).buffer")
assertParse("borrow msg")
assertParse("use(borrow msg)")
assertParse("borrow(msg)")
assertParse("borrow (msg)")
}

func testBorrowNameFunctionCallStructure1() {
assertParse(
"""
borrow(msg)
""",
substructure: FunctionCallExprSyntax(
calledExpression: DeclReferenceExprSyntax(baseName: .identifier("borrow")),
leftParen: .leftParenToken(),
arguments: LabeledExprListSyntax([LabeledExprSyntax(expression: ExprSyntax("msg"))]),
rightParen: .rightParenToken()
)
)
}

func testBorrowNameFunctionCallStructure2() {
assertParse(
"""
borrow (msg)
""",
substructure: FunctionCallExprSyntax(
calledExpression: DeclReferenceExprSyntax(baseName: .identifier("borrow")),
leftParen: .leftParenToken(),
arguments: LabeledExprListSyntax([LabeledExprSyntax(expression: ExprSyntax("msg"))]),
rightParen: .rightParenToken()
)
)
}

func testCodeCompletionExpressions() {
Expand Down