Skip to content

Commit 5c870b4

Browse files
authored
Merge pull request #1363 from gottesmm/pr-56d5f24cde74ce112ffcd40bc4c23e08e70aae99
[reference-bindings] Add support for borrow, inout bindings.
2 parents e1fb17f + 71a621a commit 5c870b4

File tree

16 files changed

+105
-29
lines changed

16 files changed

+105
-29
lines changed

CodeGeneration/Sources/SyntaxSupport/gyb_generated/DeclNodes.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ public let DECL_NODES: [Node] = [
576576
Child(name: "ImportTok",
577577
kind: .token(choices: [.keyword(text: "import")])),
578578
Child(name: "ImportKind",
579-
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")]),
579+
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")]),
580580
isOptional: true),
581581
Child(name: "Path",
582582
kind: .collection(kind: "AccessPath", collectionElementName: "PathComponent"))
@@ -1216,7 +1216,7 @@ public let DECL_NODES: [Node] = [
12161216
nameForDiagnostics: "modifiers",
12171217
isOptional: true),
12181218
Child(name: "BindingKeyword",
1219-
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var")])),
1219+
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var"), .keyword(text: "inout")])),
12201220
Child(name: "Bindings",
12211221
kind: .collection(kind: "PatternBindingList", collectionElementName: "Binding"))
12221222
]),

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
@@ -273,7 +273,7 @@ public let STMT_NODES: [Node] = [
273273
kind: "Syntax",
274274
children: [
275275
Child(name: "BindingKeyword",
276-
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var")])),
276+
kind: .token(choices: [.keyword(text: "let"), .keyword(text: "var"), .keyword(text: "inout")])),
277277
Child(name: "Pattern",
278278
kind: .node(kind: "Pattern")),
279279
Child(name: "TypeAnnotation",

Sources/SwiftParser/Declarations.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,8 @@ extension Parser {
243243
return RawDeclSyntax(self.parseFuncDeclaration(attrs, handle))
244244
case (.subscriptKeyword, let handle)?:
245245
return RawDeclSyntax(self.parseSubscriptDeclaration(attrs, handle))
246-
case (.letKeyword, let handle)?, (.varKeyword, let handle)?:
246+
case (.letKeyword, let handle)?, (.varKeyword, let handle)?,
247+
(.inoutKeyword, let handle)?:
247248
return RawDeclSyntax(self.parseBindingDeclaration(attrs, handle, inMemberDeclList: inMemberDeclList))
248249
case (.initKeyword, let handle)?:
249250
return RawDeclSyntax(self.parseInitializerDeclaration(attrs, handle))
@@ -332,6 +333,7 @@ extension Parser {
332333
case `var`
333334
case `let`
334335
case `func`
336+
case `inout`
335337

336338
var spec: TokenSpec {
337339
switch self {
@@ -343,6 +345,7 @@ extension Parser {
343345
case .var: return .keyword(.var)
344346
case .let: return .keyword(.let)
345347
case .func: return .keyword(.func)
348+
case .inout: return .keyword(.inout)
346349
}
347350
}
348351

@@ -356,6 +359,7 @@ extension Parser {
356359
case TokenSpec(.var): self = .var
357360
case TokenSpec(.let): self = .let
358361
case TokenSpec(.func): self = .func
362+
case TokenSpec(.inout): self = .inout
359363
default: return nil
360364
}
361365
}

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
@@ -216,17 +216,18 @@ extension Parser {
216216
/// condition → expression | availability-condition | case-condition | optional-binding-condition
217217
///
218218
/// case-condition → 'case' pattern initializer
219-
/// optional-binding-condition → 'let' pattern initializer? | 'var' pattern initializer?
219+
/// optional-binding-condition → 'let' pattern initializer? | 'var' pattern initializer? |
220+
/// 'inout' pattern initializer?
220221
@_spi(RawSyntax)
221222
public mutating func parseConditionElement() -> RawConditionElementSyntax.Condition {
222223
// Parse a leading #available/#unavailable condition if present.
223224
if self.at(.poundAvailableKeyword, .poundUnavailableKeyword) {
224225
return self.parsePoundAvailableConditionElement()
225226
}
226227

227-
// Parse the basic expression case. If we have a leading let/var/case
228-
// keyword or an assignment, then we know this is a binding.
229-
guard self.at(.keyword(.let), .keyword(.var), .keyword(.case)) else {
228+
// Parse the basic expression case. If we have a leading let, var, inout,
229+
// borrow, case keyword or an assignment, then we know this is a binding.
230+
guard self.at(.keyword(.let), .keyword(.var), .keyword(.case)) || self.at(.keyword(.inout)) else {
230231
// If we lack it, then this is theoretically a boolean condition.
231232
// However, we also need to handle migrating from Swift 2 syntax, in
232233
// which a comma followed by an expression could actually be a pattern
@@ -243,7 +244,7 @@ extension Parser {
243244
}
244245

245246
// We're parsing a conditional binding.
246-
assert(self.at(.keyword(.let), .keyword(.var), .keyword(.case)))
247+
assert(self.at(.keyword(.let), .keyword(.var)) || self.at(.keyword(.inout), .keyword(.case)))
247248
enum BindingKind {
248249
case pattern(RawTokenSyntax, RawPatternSyntax)
249250
case optional(RawTokenSyntax, RawPatternSyntax)
@@ -255,7 +256,7 @@ extension Parser {
255256
kind = .pattern(caseKeyword, pattern)
256257
} else {
257258
let letOrVar = self.consumeAnyToken()
258-
let pattern = self.parseMatchingPattern(context: .letOrVar)
259+
let pattern = self.parseMatchingPattern(context: .bindingIntroducer)
259260
kind = .optional(letOrVar, pattern)
260261
}
261262

@@ -288,10 +289,10 @@ extension Parser {
288289
}
289290

290291
switch kind {
291-
case let .optional(letOrVar, pattern):
292+
case let .optional(bindingKeyword, pattern):
292293
return .optionalBinding(
293294
RawOptionalBindingConditionSyntax(
294-
bindingKeyword: letOrVar,
295+
bindingKeyword: bindingKeyword,
295296
pattern: pattern,
296297
typeAnnotation: annotation,
297298
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
@@ -244,6 +244,7 @@ enum DeclarationStart: TokenSpecSet {
244244
case subscriptKeyword
245245
case typealiasKeyword
246246
case varKeyword
247+
case inoutKeyword
247248

248249
init?(lexeme: Lexer.Lexeme) {
249250
switch PrepareForKeywordMatch(lexeme) {
@@ -266,6 +267,7 @@ enum DeclarationStart: TokenSpecSet {
266267
case TokenSpec(.subscript): self = .subscriptKeyword
267268
case TokenSpec(.typealias): self = .typealiasKeyword
268269
case TokenSpec(.var): self = .varKeyword
270+
case TokenSpec(.inout): self = .inoutKeyword
269271
default: return nil
270272
}
271273
}
@@ -291,6 +293,7 @@ enum DeclarationStart: TokenSpecSet {
291293
case .subscriptKeyword: return .keyword(.subscript)
292294
case .typealiasKeyword: return .keyword(.typealias)
293295
case .varKeyword: return .keyword(.var)
296+
case .inoutKeyword: return TokenSpec(.inout, recoveryPrecedence: .declKeyword)
294297
}
295298
}
296299
}
@@ -584,12 +587,14 @@ enum MatchingPatternStart: TokenSpecSet {
584587
case isKeyword
585588
case letKeyword
586589
case varKeyword
590+
case inoutKeyword
587591

588592
init?(lexeme: Lexer.Lexeme) {
589593
switch PrepareForKeywordMatch(lexeme) {
590594
case TokenSpec(.is): self = .isKeyword
591595
case TokenSpec(.let): self = .letKeyword
592596
case TokenSpec(.var): self = .varKeyword
597+
case TokenSpec(.inout): self = .inoutKeyword
593598
default: return nil
594599
}
595600
}
@@ -599,6 +604,7 @@ enum MatchingPatternStart: TokenSpecSet {
599604
case .isKeyword: return .keyword(.is)
600605
case .letKeyword: return .keyword(.let)
601606
case .varKeyword: return .keyword(.var)
607+
case .inoutKeyword: return .keyword(.inout)
602608
}
603609
}
604610
}

Sources/SwiftParser/Types.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,10 @@ extension Parser.Lookahead {
786786

787787
mutating func canParseTupleBodyType() -> Bool {
788788
guard
789-
!self.at(.rightParen, .rightBrace) && !self.atContextualPunctuator("...") && !self.atStartOfDeclaration()
789+
!self.at(.rightParen, .rightBrace) && !self.atContextualPunctuator("...")
790+
// In types, we do not allow for an inout binding to be declared in a
791+
// tuple type.
792+
&& (self.at(.keyword(.inout)) || !self.atStartOfDeclaration())
790793
else {
791794
return self.consume(if: .rightParen) != nil
792795
}

Tests/SwiftParserTest/translated/MatchingPatternsTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ final class MatchingPatternsTests: XCTestCase {
6565
a = 1
6666
case let a:
6767
a = 1
68+
case inout a:
69+
a = 1
6870
case var var a:
6971
a += 1
7072
case var let a:
@@ -181,6 +183,7 @@ final class MatchingPatternsTests: XCTestCase {
181183
var n : Voluntary<Int> = .Naught
182184
if case let .Naught(value) = n {}
183185
if case let .Naught(value1, value2, value3) = n {}
186+
if case inout .Naught(value) = n {}
184187
"""
185188
)
186189
}
@@ -357,6 +360,8 @@ final class MatchingPatternsTests: XCTestCase {
357360
case .Payload((let x)):
358361
acceptInt(x)
359362
acceptString("\(x)")
363+
case .Payload(inout x):
364+
acceptInt(x)
360365
}
361366
"""#
362367
)
@@ -420,6 +425,8 @@ final class MatchingPatternsTests: XCTestCase {
420425
switch t {
421426
case (_, var a, 3):
422427
a += 1
428+
case (_, inout a, 3):
429+
a += 1
423430
case var (_, b, 3):
424431
b += 1
425432
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)