Skip to content

Commit a1896bd

Browse files
committed
[reference-bindings] Add support for borrow, inout bindings.
To make sure that I did not stomp on anything already working around borrow, inout, I decided to (for now) use the keywords inoutBindingKW and borrowBindingKW, just to be careful.
1 parent bcaad6d commit a1896bd

File tree

19 files changed

+202
-29
lines changed

19 files changed

+202
-29
lines changed

CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public let KEYWORDS: [KeywordSpec] = [
100100
KeywordSpec("backDeployed"),
101101
KeywordSpec("before"),
102102
KeywordSpec("block"),
103+
KeywordSpec("borrowBindingKW", isLexerClassified: true, requiresTrailingSpace: true),
103104
KeywordSpec("break", isLexerClassified: true, requiresTrailingSpace: true),
104105
KeywordSpec("case", isLexerClassified: true, requiresTrailingSpace: true),
105106
KeywordSpec("catch", isLexerClassified: true, requiresLeadingSpace: true),
@@ -144,6 +145,7 @@ public let KEYWORDS: [KeywordSpec] = [
144145
KeywordSpec("init", isLexerClassified: true),
145146
KeywordSpec("inline"),
146147
KeywordSpec("inout", isLexerClassified: true, requiresTrailingSpace: true),
148+
KeywordSpec("inoutBindingKW", isLexerClassified: true, requiresTrailingSpace: true),
147149
KeywordSpec("internal", isLexerClassified: true, requiresTrailingSpace: true),
148150
KeywordSpec("introduced"),
149151
KeywordSpec("is", isLexerClassified: true, requiresTrailingSpace: true),

CodeGeneration/Sources/SyntaxSupport/gyb_generated/DeclNodes.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ public let DECL_NODES: [Node] = [
574574
Child(name: "ImportTok",
575575
kind: .token(choices: [.keyword(text: "import")])),
576576
Child(name: "ImportKind",
577-
kind: .token(choices: [.keyword(text: "typealias"), .keyword(text: "struct"), .keyword(text: "class"), .keyword(text: "enum"), .keyword(text: "protocol"), .keyword(text: "var"), .keyword(text: "let"), .keyword(text: "func")]),
577+
kind: .token(choices: [.keyword(text: "typealias"), .keyword(text: "struct"), .keyword(text: "class"), .keyword(text: "enum"), .keyword(text: "protocol"), .keyword(text: "var"), .keyword(text: "let"), .keyword(text: "func"), .keyword(text: "borrowBindingKW"), .keyword(text: "inoutBindingKW")]),
578578
isOptional: true),
579579
Child(name: "Path",
580580
kind: .collection(kind: "AccessPath", collectionElementName: "PathComponent"))
@@ -1214,7 +1214,7 @@ public let DECL_NODES: [Node] = [
12141214
nameForDiagnostics: "modifiers",
12151215
isOptional: true),
12161216
Child(name: "BindingKeyword",
1217-
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var")])),
1217+
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var"), .keyword(text: "borrowBindingKW"), .keyword(text: "inoutBindingKW")])),
12181218
Child(name: "Bindings",
12191219
kind: .collection(kind: "PatternBindingList", collectionElementName: "Binding"))
12201220
]),

CodeGeneration/Sources/SyntaxSupport/gyb_generated/PatternNodes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public let PATTERN_NODES: [Node] = [
9595
kind: "Pattern",
9696
children: [
9797
Child(name: "BindingKeyword",
98-
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var")])),
98+
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var"), .keyword(text: "borrowBindingKW"), .keyword(text: "inoutBindingKW|")])),
9999
Child(name: "ValuePattern",
100100
kind: .node(kind: "Pattern"))
101101
]),

CodeGeneration/Sources/SyntaxSupport/gyb_generated/StmtNodes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ public let STMT_NODES: [Node] = [
263263
kind: "Syntax",
264264
children: [
265265
Child(name: "BindingKeyword",
266-
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var")])),
266+
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var"), .keyword(text: "borrowBindingKW"), .keyword(text: "inoutBindingKW")])),
267267
Child(name: "Pattern",
268268
kind: .node(kind: "Pattern")),
269269
Child(name: "TypeAnnotation",

Sources/SwiftBasicFormat/generated/BasicFormat.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ open class BasicFormat: SyntaxRewriter {
270270
return true
271271
case .keyword(.async):
272272
return true
273+
case .keyword(.`borrowBindingKW`):
274+
return true
273275
case .keyword(.`break`):
274276
return true
275277
case .keyword(.`case`):
@@ -304,6 +306,8 @@ open class BasicFormat: SyntaxRewriter {
304306
return true
305307
case .keyword(.`inout`):
306308
return true
309+
case .keyword(.`inoutBindingKW`):
310+
return true
307311
case .keyword(.`internal`):
308312
return true
309313
case .keyword(.`is`):

Sources/SwiftParser/Declarations.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ extension Parser {
241241
return RawDeclSyntax(self.parseFuncDeclaration(attrs, handle))
242242
case (.subscriptKeyword, let handle)?:
243243
return RawDeclSyntax(self.parseSubscriptDeclaration(attrs, handle))
244-
case (.letKeyword, let handle)?, (.varKeyword, let handle)?:
244+
case (.letKeyword, let handle)?, (.varKeyword, let handle)?,
245+
(.borrowKeyword, let handle)?, (.inoutKeyword, let handle)?:
245246
return RawDeclSyntax(self.parseBindingDeclaration(attrs, handle, inMemberDeclList: inMemberDeclList))
246247
case (.initKeyword, let handle)?:
247248
return RawDeclSyntax(self.parseInitializerDeclaration(attrs, handle))
@@ -330,6 +331,8 @@ extension Parser {
330331
case `var`
331332
case `let`
332333
case `func`
334+
case `borrow`
335+
case `inout`
333336

334337
var spec: TokenSpec {
335338
switch self {
@@ -341,6 +344,8 @@ extension Parser {
341344
case .var: return .keyword(.var)
342345
case .let: return .keyword(.let)
343346
case .func: return .keyword(.func)
347+
case .`inout`: return .keyword(.inoutBindingKW)
348+
case .borrow: return .keyword(.borrowBindingKW)
344349
}
345350
}
346351

@@ -354,6 +359,8 @@ extension Parser {
354359
case TokenSpec(.var): self = .var
355360
case TokenSpec(.let): self = .let
356361
case TokenSpec(.func): self = .func
362+
case TokenSpec(.borrowBindingKW): self = .borrow
363+
case TokenSpec(.inoutBindingKW): self = .`inout`
357364
default: return nil
358365
}
359366
}

Sources/SwiftParser/Expressions.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,16 @@ extension Parser {
7575
/// case x.y <- 'x' must refer to some 'x' defined in another scope, it cannot be e.g. an enum type.
7676
/// ```
7777
case matching
78-
/// We're parsing a matching pattern that is introduced via `let` or `var`.
78+
/// We're parsing a matching pattern that is introduced via `let`, `var`, `borrow`, or `inout`
7979
///
8080
/// ```
8181
/// case let x.y <- 'x' must refer to the base of some member access, y must refer to some pattern-compatible identfier
8282
/// ```
83-
case letOrVar
83+
case bindingIntroducer
8484

8585
var admitsBinding: Bool {
8686
switch self {
87-
case .letOrVar:
87+
case .bindingIntroducer:
8888
return true
8989
case .none, .matching:
9090
return false
@@ -328,7 +328,7 @@ extension Parser {
328328

329329
case (.equal, let handle)?:
330330
switch pattern {
331-
case .matching, .letOrVar:
331+
case .matching, .bindingIntroducer:
332332
return nil
333333
case .none:
334334
let eq = self.eat(handle)

Sources/SwiftParser/Patterns.swift

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ extension Parser {
5353
case dollarIdentifier // For recovery
5454
case letKeyword
5555
case varKeyword
56+
case inoutKeyword
57+
case borrowKeyword
5658

5759
init?(lexeme: Lexer.Lexeme) {
5860
switch PrepareForKeywordMatch(lexeme) {
@@ -62,6 +64,8 @@ extension Parser {
6264
case TokenSpec(.dollarIdentifier): self = .dollarIdentifier
6365
case TokenSpec(.let): self = .letKeyword
6466
case TokenSpec(.var): self = .varKeyword
67+
case TokenSpec(.borrowBindingKW): self = .borrowKeyword
68+
case TokenSpec(.inoutBindingKW): self = .inoutKeyword
6569
default: return nil
6670
}
6771
}
@@ -74,6 +78,8 @@ extension Parser {
7478
case .dollarIdentifier: return .dollarIdentifier
7579
case .letKeyword: return .keyword(.let)
7680
case .varKeyword: return .keyword(.var)
81+
case .borrowKeyword: return .keyword(.borrowBindingKW)
82+
case .inoutKeyword: return .keyword(.inoutBindingKW)
7783
}
7884
}
7985
}
@@ -120,12 +126,14 @@ extension Parser {
120126
)
121127
)
122128
case (.letKeyword, let handle)?,
123-
(.varKeyword, let handle)?:
124-
let letOrVar = self.eat(handle)
129+
(.varKeyword, let handle)?,
130+
(.borrowKeyword, let handle)?,
131+
(.inoutKeyword, let handle)?:
132+
let bindingKeyword = self.eat(handle)
125133
let value = self.parsePattern()
126134
return RawPatternSyntax(
127135
RawValueBindingPatternSyntax(
128-
bindingKeyword: letOrVar,
136+
bindingKeyword: bindingKeyword,
129137
valuePattern: value,
130138
arena: self.arena
131139
)
@@ -239,12 +247,14 @@ extension Parser {
239247
// Parse productions that can only be patterns.
240248
switch self.at(anyIn: MatchingPatternStart.self) {
241249
case (.varKeyword, let handle)?,
242-
(.letKeyword, let handle)?:
243-
let letOrVar = self.eat(handle)
244-
let value = self.parseMatchingPattern(context: .letOrVar)
250+
(.letKeyword, let handle)?,
251+
(.borrowKeyword, let handle)?,
252+
(.inoutKeyword, let handle)?:
253+
let bindingKeyword = self.eat(handle)
254+
let value = self.parseMatchingPattern(context: .bindingIntroducer)
245255
return RawPatternSyntax(
246256
RawValueBindingPatternSyntax(
247-
bindingKeyword: letOrVar,
257+
bindingKeyword: bindingKeyword,
248258
valuePattern: value,
249259
arena: self.arena
250260
)
@@ -287,12 +297,16 @@ extension Parser.Lookahead {
287297
/// pattern ::= pattern-tuple
288298
/// pattern ::= 'var' pattern
289299
/// pattern ::= 'let' pattern
300+
/// pattern ::= 'borrow' pattern
301+
/// pattern ::= 'inout' pattern
290302
mutating func canParsePattern() -> Bool {
291303
enum PatternStartTokens: TokenSpecSet {
292304
case identifier
293305
case wildcard
294306
case letKeyword
295307
case varKeyword
308+
case inoutKeyword
309+
case borrowKeyword
296310
case leftParen
297311

298312
init?(lexeme: Lexer.Lexeme) {
@@ -301,6 +315,8 @@ extension Parser.Lookahead {
301315
case TokenSpec(.wildcard): self = .wildcard
302316
case TokenSpec(.let): self = .letKeyword
303317
case TokenSpec(.var): self = .varKeyword
318+
case TokenSpec(.inoutBindingKW): self = .inoutKeyword
319+
case TokenSpec(.borrowBindingKW): self = .borrowKeyword
304320
case TokenSpec(.leftParen): self = .leftParen
305321
default: return nil
306322
}
@@ -312,6 +328,8 @@ extension Parser.Lookahead {
312328
case .wildcard: return .wildcard
313329
case .letKeyword: return .keyword(.let)
314330
case .varKeyword: return .keyword(.var)
331+
case .borrowKeyword: return .keyword(.borrowBindingKW)
332+
case .inoutKeyword: return .keyword(.inoutBindingKW)
315333
case .leftParen: return .leftParen
316334
}
317335
}
@@ -323,7 +341,9 @@ extension Parser.Lookahead {
323341
self.eat(handle)
324342
return true
325343
case (.letKeyword, let handle)?,
326-
(.varKeyword, let handle)?:
344+
(.varKeyword, let handle)?,
345+
(.borrowKeyword, let handle)?,
346+
(.inoutKeyword, let handle)?:
327347
self.eat(handle)
328348
return self.canParsePattern()
329349
case (.leftParen, _)?:

Sources/SwiftParser/Statements.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,19 @@ extension Parser {
213213
/// condition → expression | availability-condition | case-condition | optional-binding-condition
214214
///
215215
/// case-condition → 'case' pattern initializer
216-
/// optional-binding-condition → 'let' pattern initializer? | 'var' pattern initializer?
216+
/// optional-binding-condition → 'let' pattern initializer? | 'var' pattern initializer? |
217+
/// 'borrow' pattern initializer? | 'inout' pattern initializer?
217218
@_spi(RawSyntax)
218219
public mutating func parseConditionElement() -> RawConditionElementSyntax.Condition {
219220
// Parse a leading #available/#unavailable condition if present.
220221
if self.at(.poundAvailableKeyword, .poundUnavailableKeyword) {
221222
return self.parsePoundAvailableConditionElement()
222223
}
223224

224-
// Parse the basic expression case. If we have a leading let/var/case
225-
// keyword or an assignment, then we know this is a binding.
226-
guard self.at(.keyword(.let), .keyword(.var), .keyword(.case)) else {
225+
// Parse the basic expression case. If we have a leading let, var, inout,
226+
// borrow, case keyword or an assignment, then we know this is a binding.
227+
guard self.at(.keyword(.let), .keyword(.var), .keyword(.case),
228+
.keyword(.borrowBindingKW), .keyword(.inoutBindingKW)) else {
227229
// If we lack it, then this is theoretically a boolean condition.
228230
// However, we also need to handle migrating from Swift 2 syntax, in
229231
// which a comma followed by an expression could actually be a pattern
@@ -240,7 +242,8 @@ extension Parser {
240242
}
241243

242244
// We're parsing a conditional binding.
243-
assert(self.at(.keyword(.let), .keyword(.var), .keyword(.case)))
245+
assert(self.at(.keyword(.let), .keyword(.var), .keyword(.borrowBindingKW),
246+
.keyword(.inoutBindingKW), .keyword(.case)))
244247
enum BindingKind {
245248
case pattern(RawTokenSyntax, RawPatternSyntax)
246249
case optional(RawTokenSyntax, RawPatternSyntax)
@@ -252,7 +255,7 @@ extension Parser {
252255
kind = .pattern(caseKeyword, pattern)
253256
} else {
254257
let letOrVar = self.consumeAnyToken()
255-
let pattern = self.parseMatchingPattern(context: .letOrVar)
258+
let pattern = self.parseMatchingPattern(context: .bindingIntroducer)
256259
kind = .optional(letOrVar, pattern)
257260
}
258261

@@ -285,10 +288,10 @@ extension Parser {
285288
}
286289

287290
switch kind {
288-
case let .optional(letOrVar, pattern):
291+
case let .optional(bindingKeyword, pattern):
289292
return .optionalBinding(
290293
RawOptionalBindingConditionSyntax(
291-
bindingKeyword: letOrVar,
294+
bindingKeyword: bindingKeyword,
292295
pattern: pattern,
293296
typeAnnotation: annotation,
294297
initializer: initializer,

Sources/SwiftParser/TokenConsumer.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,24 @@ extension TokenConsumer {
8282
}
8383
}
8484

85+
@inline(__always)
86+
mutating func at(
87+
_ spec1: TokenSpec,
88+
_ spec2: TokenSpec,
89+
_ spec3: TokenSpec,
90+
_ spec4: TokenSpec,
91+
_ spec5: TokenSpec
92+
) -> Bool {
93+
switch self.currentToken {
94+
case spec1: return true
95+
case spec2: return true
96+
case spec3: return true
97+
case spec4: return true
98+
case spec5: return true
99+
default: return false
100+
}
101+
}
102+
85103
/// Returns whether the current token is an operator with the given `name`.
86104
@inline(__always)
87105
mutating func atContextualPunctuator(_ name: SyntaxText) -> Bool {

Sources/SwiftParser/TokenPrecedence.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ public enum TokenPrecedence: Comparable {
215215
// Functions
216216
.deinit, .func, .`init`, .subscript,
217217
// Variables
218-
.let, .var,
218+
.let, .var, .borrowBindingKW, .inoutBindingKW,
219219
// Operator stuff
220220
.operator, .precedencegroup,
221221
// Misc

Sources/SwiftParser/TokenSpecSet.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ enum DeclarationStart: TokenSpecSet {
241241
case subscriptKeyword
242242
case typealiasKeyword
243243
case varKeyword
244+
case borrowKeyword
245+
case inoutKeyword
244246

245247
init?(lexeme: Lexer.Lexeme) {
246248
switch PrepareForKeywordMatch(lexeme) {
@@ -263,6 +265,8 @@ enum DeclarationStart: TokenSpecSet {
263265
case TokenSpec(.subscript): self = .subscriptKeyword
264266
case TokenSpec(.typealias): self = .typealiasKeyword
265267
case TokenSpec(.var): self = .varKeyword
268+
case TokenSpec(.borrowBindingKW): self = .borrowKeyword
269+
case TokenSpec(.inoutBindingKW): self = .inoutKeyword
266270
default: return nil
267271
}
268272
}
@@ -288,6 +292,8 @@ enum DeclarationStart: TokenSpecSet {
288292
case .subscriptKeyword: return .keyword(.subscript)
289293
case .typealiasKeyword: return .keyword(.typealias)
290294
case .varKeyword: return .keyword(.var)
295+
case .borrowKeyword: return .keyword(.borrowBindingKW)
296+
case .inoutKeyword: return .keyword(.inoutBindingKW)
291297
}
292298
}
293299
}
@@ -570,12 +576,16 @@ enum MatchingPatternStart: TokenSpecSet {
570576
case isKeyword
571577
case letKeyword
572578
case varKeyword
579+
case borrowKeyword
580+
case inoutKeyword
573581

574582
init?(lexeme: Lexer.Lexeme) {
575583
switch PrepareForKeywordMatch(lexeme) {
576584
case TokenSpec(.is): self = .isKeyword
577585
case TokenSpec(.let): self = .letKeyword
578586
case TokenSpec(.var): self = .varKeyword
587+
case TokenSpec(.inoutBindingKW): self = .inoutKeyword
588+
case TokenSpec(.borrowBindingKW): self = .borrowKeyword
579589
default: return nil
580590
}
581591
}
@@ -585,6 +595,8 @@ enum MatchingPatternStart: TokenSpecSet {
585595
case .isKeyword: return .keyword(.is)
586596
case .letKeyword: return .keyword(.let)
587597
case .varKeyword: return .keyword(.var)
598+
case .inoutKeyword: return .keyword(.inoutBindingKW)
599+
case .borrowKeyword: return .keyword(.borrowBindingKW)
588600
}
589601
}
590602
}

0 commit comments

Comments
 (0)