Skip to content

Commit 3b021d7

Browse files
committed
[reference-bindings] Add support for inout bindings.
1 parent 7fa6666 commit 3b021d7

File tree

15 files changed

+101
-28
lines changed

15 files changed

+101
-28
lines changed

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

Sources/SwiftParser/Declarations.swift

Lines changed: 5 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+
(.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,7 @@ extension Parser {
330331
case `var`
331332
case `let`
332333
case `func`
334+
case `inout`
333335

334336
var spec: TokenSpec {
335337
switch self {
@@ -341,6 +343,7 @@ extension Parser {
341343
case .var: return .keyword(.var)
342344
case .let: return .keyword(.let)
343345
case .func: return .keyword(.func)
346+
case .inout: return .keyword(.inout)
344347
}
345348
}
346349

@@ -354,6 +357,7 @@ extension Parser {
354357
case TokenSpec(.var): self = .var
355358
case TokenSpec(.let): self = .let
356359
case TokenSpec(.func): self = .func
360+
case TokenSpec(.inout): self = .inout
357361
default: return nil
358362
}
359363
}

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`, 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: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ extension Parser {
5353
case dollarIdentifier // For recovery
5454
case letKeyword
5555
case varKeyword
56+
case inoutKeyword
5657

5758
init?(lexeme: Lexer.Lexeme) {
5859
switch PrepareForKeywordMatch(lexeme) {
@@ -62,6 +63,7 @@ extension Parser {
6263
case TokenSpec(.dollarIdentifier): self = .dollarIdentifier
6364
case TokenSpec(.let): self = .letKeyword
6465
case TokenSpec(.var): self = .varKeyword
66+
case TokenSpec(.inout): self = .inoutKeyword
6567
default: return nil
6668
}
6769
}
@@ -74,6 +76,7 @@ extension Parser {
7476
case .dollarIdentifier: return .dollarIdentifier
7577
case .letKeyword: return .keyword(.let)
7678
case .varKeyword: return .keyword(.var)
79+
case .inoutKeyword: return .keyword(.inout)
7780
}
7881
}
7982
}
@@ -120,12 +123,13 @@ extension Parser {
120123
)
121124
)
122125
case (.letKeyword, let handle)?,
123-
(.varKeyword, let handle)?:
124-
let letOrVar = self.eat(handle)
126+
(.varKeyword, let handle)?,
127+
(.inoutKeyword, let handle)?:
128+
let bindingKeyword = self.eat(handle)
125129
let value = self.parsePattern()
126130
return RawPatternSyntax(
127131
RawValueBindingPatternSyntax(
128-
bindingKeyword: letOrVar,
132+
bindingKeyword: bindingKeyword,
129133
valuePattern: value,
130134
arena: self.arena
131135
)
@@ -239,12 +243,13 @@ extension Parser {
239243
// Parse productions that can only be patterns.
240244
switch self.at(anyIn: MatchingPatternStart.self) {
241245
case (.varKeyword, let handle)?,
242-
(.letKeyword, let handle)?:
243-
let letOrVar = self.eat(handle)
244-
let value = self.parseMatchingPattern(context: .letOrVar)
246+
(.letKeyword, let handle)?,
247+
(.inoutKeyword, let handle)?:
248+
let bindingKeyword = self.eat(handle)
249+
let value = self.parseMatchingPattern(context: .bindingIntroducer)
245250
return RawPatternSyntax(
246251
RawValueBindingPatternSyntax(
247-
bindingKeyword: letOrVar,
252+
bindingKeyword: bindingKeyword,
248253
valuePattern: value,
249254
arena: self.arena
250255
)
@@ -287,13 +292,15 @@ extension Parser.Lookahead {
287292
/// pattern ::= pattern-tuple
288293
/// pattern ::= 'var' pattern
289294
/// pattern ::= 'let' pattern
295+
/// pattern ::= 'inout' pattern
290296
mutating func canParsePattern() -> Bool {
291297
enum PatternStartTokens: TokenSpecSet {
292298
case identifier
293299
case wildcard
294300
case letKeyword
295301
case varKeyword
296302
case leftParen
303+
case inoutKeyword
297304

298305
init?(lexeme: Lexer.Lexeme) {
299306
switch PrepareForKeywordMatch(lexeme) {
@@ -302,6 +309,7 @@ extension Parser.Lookahead {
302309
case TokenSpec(.let): self = .letKeyword
303310
case TokenSpec(.var): self = .varKeyword
304311
case TokenSpec(.leftParen): self = .leftParen
312+
case TokenSpec(.inout): self = .inoutKeyword
305313
default: return nil
306314
}
307315
}
@@ -313,6 +321,7 @@ extension Parser.Lookahead {
313321
case .letKeyword: return .keyword(.let)
314322
case .varKeyword: return .keyword(.var)
315323
case .leftParen: return .leftParen
324+
case .inoutKeyword: return .keyword(.inout)
316325
}
317326
}
318327
}
@@ -323,7 +332,8 @@ extension Parser.Lookahead {
323332
self.eat(handle)
324333
return true
325334
case (.letKeyword, let handle)?,
326-
(.varKeyword, let handle)?:
335+
(.varKeyword, let handle)?,
336+
(.inoutKeyword, let handle)?:
327337
self.eat(handle)
328338
return self.canParsePattern()
329339
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+
/// '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(.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)) || 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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ enum DeclarationStart: TokenSpecSet {
241241
case subscriptKeyword
242242
case typealiasKeyword
243243
case varKeyword
244+
case inoutKeyword
244245

245246
init?(lexeme: Lexer.Lexeme) {
246247
switch PrepareForKeywordMatch(lexeme) {
@@ -263,6 +264,7 @@ enum DeclarationStart: TokenSpecSet {
263264
case TokenSpec(.subscript): self = .subscriptKeyword
264265
case TokenSpec(.typealias): self = .typealiasKeyword
265266
case TokenSpec(.var): self = .varKeyword
267+
case TokenSpec(.inout): self = .inoutKeyword
266268
default: return nil
267269
}
268270
}
@@ -288,6 +290,7 @@ enum DeclarationStart: TokenSpecSet {
288290
case .subscriptKeyword: return .keyword(.subscript)
289291
case .typealiasKeyword: return .keyword(.typealias)
290292
case .varKeyword: return .keyword(.var)
293+
case .inoutKeyword: return .keyword(.inout)
291294
}
292295
}
293296
}
@@ -570,12 +573,14 @@ enum MatchingPatternStart: TokenSpecSet {
570573
case isKeyword
571574
case letKeyword
572575
case varKeyword
576+
case inoutKeyword
573577

574578
init?(lexeme: Lexer.Lexeme) {
575579
switch PrepareForKeywordMatch(lexeme) {
576580
case TokenSpec(.is): self = .isKeyword
577581
case TokenSpec(.let): self = .letKeyword
578582
case TokenSpec(.var): self = .varKeyword
583+
case TokenSpec(.inout): self = .inoutKeyword
579584
default: return nil
580585
}
581586
}
@@ -585,6 +590,7 @@ enum MatchingPatternStart: TokenSpecSet {
585590
case .isKeyword: return .keyword(.is)
586591
case .letKeyword: return .keyword(.let)
587592
case .varKeyword: return .keyword(.var)
593+
case .inoutKeyword: return .keyword(.inout)
588594
}
589595
}
590596
}

Tests/SwiftParserTest/translated/MatchingPatternsTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ final class MatchingPatternsTests: XCTestCase {
7777
a = 1
7878
case let a:
7979
a = 1
80+
case inout a:
81+
a = 1
8082
case var var a:
8183
a += 1
8284
case var let a:
@@ -193,6 +195,7 @@ final class MatchingPatternsTests: XCTestCase {
193195
var n : Voluntary<Int> = .Naught
194196
if case let .Naught(value) = n {}
195197
if case let .Naught(value1, value2, value3) = n {}
198+
if case inout .Naught(value) = n {}
196199
"""
197200
)
198201
}
@@ -369,6 +372,8 @@ final class MatchingPatternsTests: XCTestCase {
369372
case .Payload((let x)):
370373
acceptInt(x)
371374
acceptString("\(x)")
375+
case .Payload(inout x):
376+
acceptInt(x)
372377
}
373378
"""#
374379
)
@@ -432,6 +437,8 @@ final class MatchingPatternsTests: XCTestCase {
432437
switch t {
433438
case (_, var a, 3):
434439
a += 1
440+
case (_, inout a, 3):
441+
a += 1
435442
case var (_, b, 3):
436443
b += 1
437444
case var (_, var c, 3):

Tests/SwiftParserTest/translated/PatternWithoutVariablesTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ final class PatternWithoutVariablesTests: XCTestCase {
1919
AssertParse(
2020
"""
2121
let _ = 1
22+
inout _ = 1
2223
"""
2324
)
2425
}
@@ -28,6 +29,7 @@ final class PatternWithoutVariablesTests: XCTestCase {
2829
"""
2930
func foo() {
3031
let _ = 1 // OK
32+
inout _ = 1
3133
}
3234
"""
3335
)
@@ -42,6 +44,7 @@ final class PatternWithoutVariablesTests: XCTestCase {
4244
func foo() {
4345
let _ = 1 // OK
4446
}
47+
inout (_, _) = (1, 2)
4548
}
4649
"""
4750
)
@@ -73,6 +76,9 @@ final class PatternWithoutVariablesTests: XCTestCase {
7376
case let (_, x): _ = x; break // ok
7477
}
7578
if case let _ = "str" {}
79+
switch a {
80+
case inout .Bar: break
81+
}
7682
}
7783
"""#
7884
)

0 commit comments

Comments
 (0)