Skip to content

Commit db1a31c

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 db1a31c

File tree

17 files changed

+169
-29
lines changed

17 files changed

+169
-29
lines changed

CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public let KEYWORDS: [KeywordSpec] = [
4343
KeywordSpec("__shared"),
4444
KeywordSpec("_alignment"),
4545
KeywordSpec("_backDeploy"),
46-
KeywordSpec("_borrow"),
46+
KeywordSpec("_borrow", requiresTrailingSpace: true),
4747
KeywordSpec("_cdecl"),
4848
KeywordSpec("_Class"),
4949
KeywordSpec("_compilerInitialized"),

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: "_borrow"), .keyword(text: "inout")]),
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: "_borrow"), .keyword(text: "inout")])),
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: "_borrow"), .keyword(text: "inout|")])),
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: "_borrow"), .keyword(text: "inout")])),
267267
Child(name: "Pattern",
268268
kind: .node(kind: "Pattern")),
269269
Child(name: "TypeAnnotation",

Sources/SwiftBasicFormat/generated/BasicFormat.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ open class BasicFormat: SyntaxRewriter {
262262
return true
263263
case .poundUnavailableKeyword:
264264
return true
265+
case .keyword(._borrow):
266+
return true
265267
case .keyword(.`Any`):
266268
return true
267269
case .keyword(.`as`):

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(.inout)
348+
case .borrow: return .keyword(._borrow)
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(._borrow): self = .borrow
363+
case TokenSpec(.inout): 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(._borrow): self = .borrowKeyword
68+
case TokenSpec(.inout): 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(._borrow)
82+
case .inoutKeyword: return .keyword(.inout)
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(.inout): self = .inoutKeyword
319+
case TokenSpec(._borrow): 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(._borrow)
332+
case .inoutKeyword: return .keyword(.inout)
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: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,18 @@ 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)) || self.at(.keyword(._borrow), .keyword(.inout)) else {
227228
// If we lack it, then this is theoretically a boolean condition.
228229
// However, we also need to handle migrating from Swift 2 syntax, in
229230
// which a comma followed by an expression could actually be a pattern
@@ -240,7 +241,7 @@ extension Parser {
240241
}
241242

242243
// We're parsing a conditional binding.
243-
assert(self.at(.keyword(.let), .keyword(.var), .keyword(.case)))
244+
assert(self.at(.keyword(.let), .keyword(.var), .keyword(._borrow)) || self.at(.keyword(.inout), .keyword(.case)))
244245
enum BindingKind {
245246
case pattern(RawTokenSyntax, RawPatternSyntax)
246247
case optional(RawTokenSyntax, RawPatternSyntax)
@@ -252,7 +253,7 @@ extension Parser {
252253
kind = .pattern(caseKeyword, pattern)
253254
} else {
254255
let letOrVar = self.consumeAnyToken()
255-
let pattern = self.parseMatchingPattern(context: .letOrVar)
256+
let pattern = self.parseMatchingPattern(context: .bindingIntroducer)
256257
kind = .optional(letOrVar, pattern)
257258
}
258259

@@ -285,10 +286,10 @@ extension Parser {
285286
}
286287

287288
switch kind {
288-
case let .optional(letOrVar, pattern):
289+
case let .optional(bindingKeyword, pattern):
289290
return .optionalBinding(
290291
RawOptionalBindingConditionSyntax(
291-
bindingKeyword: letOrVar,
292+
bindingKeyword: bindingKeyword,
292293
pattern: pattern,
293294
typeAnnotation: annotation,
294295
initializer: initializer,

Sources/SwiftParser/TokenPrecedence.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ public enum TokenPrecedence: Comparable {
192192
// Keywords in function types (we should be allowed to skip them inside parenthesis)
193193
.rethrows, .throws,
194194
// Consider 'any' and 'inout' like a prefix operator to a type and a type is expression-like.
195+
//
196+
// NOTE: We reuse this for inout bindings and choose the higher precedence level of expr keywords
197+
// so we do not break anything.
195198
.inout,
196199
// Consider 'any' and 'inout' like a prefix operator to a type and a type is expression-like.
197200
.Any,

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(._borrow): self = .borrowKeyword
269+
case TokenSpec(.inout): 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(._borrow)
296+
case .inoutKeyword: return .keyword(.inout)
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(.inout): self = .inoutKeyword
588+
case TokenSpec(._borrow): 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(.inout)
599+
case .borrowKeyword: return .keyword(._borrow)
588600
}
589601
}
590602
}

Tests/SwiftParserTest/translated/MatchingPatternsTests.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ final class MatchingPatternsTests: XCTestCase {
7777
a = 1
7878
case let a:
7979
a = 1
80+
case inout a:
81+
a = 1
82+
case _borrow a:
83+
a = 1
8084
case var var a:
8185
a += 1
8286
case var let a:
@@ -193,6 +197,8 @@ final class MatchingPatternsTests: XCTestCase {
193197
var n : Voluntary<Int> = .Naught
194198
if case let .Naught(value) = n {}
195199
if case let .Naught(value1, value2, value3) = n {}
200+
if case inout .Naught(value) = n {}
201+
if case _borrow .Naught(value) = n {}
196202
"""
197203
)
198204
}
@@ -369,6 +375,10 @@ final class MatchingPatternsTests: XCTestCase {
369375
case .Payload((let x)):
370376
acceptInt(x)
371377
acceptString("\(x)")
378+
case .Payload(_borrow x):
379+
acceptInt(x)
380+
case .Payload(inout x):
381+
acceptInt(x)
372382
}
373383
"""#
374384
)
@@ -432,6 +442,10 @@ final class MatchingPatternsTests: XCTestCase {
432442
switch t {
433443
case (_, var a, 3):
434444
a += 1
445+
case (_, inout a, 3):
446+
a += 1
447+
case (_, _borrow a, 3):
448+
a += 1
435449
case var (_, b, 3):
436450
b += 1
437451
case var (_, var c, 3):

0 commit comments

Comments
 (0)