Skip to content

Commit 8caf999

Browse files
authored
Merge pull request #66718 from hamishknight/getting-out-of-a-bind
2 parents f527f72 + 6fffd96 commit 8caf999

File tree

3 files changed

+94
-8
lines changed

3 files changed

+94
-8
lines changed

lib/Parse/ParseExpr.cpp

+25-8
Original file line numberDiff line numberDiff line change
@@ -1664,19 +1664,35 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
16641664
}
16651665

16661666
case tok::identifier: // foo
1667-
case tok::kw_self: // self
1668-
1667+
case tok::kw_self: { // self
1668+
auto canParseBindingInPattern = [&]() {
1669+
if (InBindingPattern != PatternBindingState::ImplicitlyImmutable &&
1670+
!InBindingPattern.getIntroducer().hasValue()) {
1671+
return false;
1672+
}
1673+
// If we have "case let x.", "case let x(", or "case let x[", we parse 'x'
1674+
// as a normal name, not a binding, because it is the start of an enum
1675+
// pattern, call, or subscript.
1676+
if (peekToken().isAny(tok::period, tok::period_prefix, tok::l_paren,
1677+
tok::l_square)) {
1678+
return false;
1679+
}
1680+
// If we have a generic argument list, this is something like
1681+
// "case let E<Int>.e(y)", and 'E' should be parsed as a normal name, not
1682+
// a binding.
1683+
if (peekToken().isAnyOperator() && peekToken().getText().equals("<")) {
1684+
BacktrackingScope S(*this);
1685+
consumeToken();
1686+
return !canParseAsGenericArgumentList();
1687+
}
1688+
return true;
1689+
}();
16691690
// If we are parsing a refutable pattern and are inside a let/var pattern,
16701691
// the identifiers change to be value bindings instead of decl references.
16711692
// Parse and return this as an UnresolvedPatternExpr around a binding. This
16721693
// will be resolved (or rejected) by sema when the overall refutable pattern
16731694
// it transformed from an expression into a pattern.
1674-
if ((InBindingPattern == PatternBindingState::ImplicitlyImmutable ||
1675-
InBindingPattern.getIntroducer().hasValue()) &&
1676-
// If we have "case let x." or "case let x(", we parse x as a normal
1677-
// name, not a binding, because it is the start of an enum pattern or
1678-
// call pattern.
1679-
peekToken().isNot(tok::period, tok::period_prefix, tok::l_paren)) {
1695+
if (canParseBindingInPattern) {
16801696
Identifier name;
16811697
SourceLoc loc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/false);
16821698
// If we have an inout/let/var, set that as our introducer. otherwise
@@ -1710,6 +1726,7 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
17101726
}
17111727

17121728
LLVM_FALLTHROUGH;
1729+
}
17131730
case tok::kw_Self: // Self
17141731
return parseExprIdentifier();
17151732

test/Constraints/rdar108738034.swift

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// rdar://108738034: Make sure we can type-check this.
4+
enum E<T>: Error {
5+
case e(T)
6+
}
7+
8+
struct S {
9+
func bar(_: (Error?) -> Void) {}
10+
}
11+
12+
func foo(_ s: S) {
13+
s.bar { error in
14+
guard let error = error else {
15+
return
16+
}
17+
if case let E<Int>.e(y) = error {
18+
print(y)
19+
}
20+
}
21+
}

test/Parse/matching_patterns.swift

+48
Original file line numberDiff line numberDiff line change
@@ -364,3 +364,51 @@ let (responseObject: Int?) = op1
364364
// expected-error @-1 {{expected ',' separator}} {{25-25=,}}
365365
// expected-error @-2 {{expected pattern}}
366366
// expected-error @-3 {{cannot convert value of type 'Int?' to specified type '(responseObject: _)'}}
367+
368+
enum E<T> {
369+
case e(T)
370+
}
371+
372+
// rdar://108738034 - Make sure we don't treat 'E' as a binding, but can treat
373+
// 'y' as a binding
374+
func testNonBinding1(_ x: E<Int>) -> Int {
375+
if case let E<Int>.e(y) = x { y } else { 0 }
376+
}
377+
378+
func testNonBinding2(_ e: E<Int>) -> Int {
379+
switch e {
380+
case let E<Int>.e(y):
381+
y
382+
}
383+
}
384+
385+
// In this case, 'y' should be an identifier, but 'z' is a binding.
386+
func testNonBinding3(_ x: (Int, Int), y: [Int]) -> Int {
387+
if case let (y[0], z) = x { z } else { 0 }
388+
}
389+
390+
func testNonBinding4(_ x: (Int, Int), y: [Int]) -> Int {
391+
switch x {
392+
case let (y[0], z):
393+
z
394+
default:
395+
0
396+
}
397+
}
398+
399+
func testNonBinding5(_ x: Int, y: [Int]) {
400+
// We treat 'z' here as a binding, which is invalid.
401+
if case let y[z] = x {} // expected-error {{pattern variable binding cannot appear in an expression}}
402+
}
403+
404+
func testNonBinding6(y: [Int], z: Int) -> Int {
405+
switch 0 {
406+
// We treat 'z' here as a binding, which is invalid.
407+
case let y[z]: // expected-error {{pattern variable binding cannot appear in an expression}}
408+
z
409+
case y[z]: // This is fine
410+
0
411+
default:
412+
0
413+
}
414+
}

0 commit comments

Comments
 (0)