diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 426c96b09c094..82a98eeb48867 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1664,19 +1664,35 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { } case tok::identifier: // foo - case tok::kw_self: // self - + case tok::kw_self: { // self + auto canParseBindingInPattern = [&]() { + if (InBindingPattern != PatternBindingState::ImplicitlyImmutable && + !InBindingPattern.getIntroducer().hasValue()) { + return false; + } + // If we have "case let x.", "case let x(", or "case let x[", we parse 'x' + // as a normal name, not a binding, because it is the start of an enum + // pattern, call, or subscript. + if (peekToken().isAny(tok::period, tok::period_prefix, tok::l_paren, + tok::l_square)) { + return false; + } + // If we have a generic argument list, this is something like + // "case let E.e(y)", and 'E' should be parsed as a normal name, not + // a binding. + if (peekToken().isAnyOperator() && peekToken().getText().equals("<")) { + BacktrackingScope S(*this); + consumeToken(); + return !canParseAsGenericArgumentList(); + } + return true; + }(); // If we are parsing a refutable pattern and are inside a let/var pattern, // the identifiers change to be value bindings instead of decl references. // Parse and return this as an UnresolvedPatternExpr around a binding. This // will be resolved (or rejected) by sema when the overall refutable pattern // it transformed from an expression into a pattern. - if ((InBindingPattern == PatternBindingState::ImplicitlyImmutable || - InBindingPattern.getIntroducer().hasValue()) && - // If we have "case let x." or "case let x(", we parse x as a normal - // name, not a binding, because it is the start of an enum pattern or - // call pattern. - peekToken().isNot(tok::period, tok::period_prefix, tok::l_paren)) { + if (canParseBindingInPattern) { Identifier name; SourceLoc loc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/false); // If we have an inout/let/var, set that as our introducer. otherwise @@ -1710,6 +1726,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { } LLVM_FALLTHROUGH; + } case tok::kw_Self: // Self return parseExprIdentifier(); diff --git a/test/Constraints/rdar108738034.swift b/test/Constraints/rdar108738034.swift new file mode 100644 index 0000000000000..368eb480884b3 --- /dev/null +++ b/test/Constraints/rdar108738034.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://108738034: Make sure we can type-check this. +enum E: Error { + case e(T) +} + +struct S { + func bar(_: (Error?) -> Void) {} +} + +func foo(_ s: S) { + s.bar { error in + guard let error = error else { + return + } + if case let E.e(y) = error { + print(y) + } + } +} diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index 050916fac0574..2a78444f7719e 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -364,3 +364,51 @@ let (responseObject: Int?) = op1 // expected-error @-1 {{expected ',' separator}} {{25-25=,}} // expected-error @-2 {{expected pattern}} // expected-error @-3 {{cannot convert value of type 'Int?' to specified type '(responseObject: _)'}} + +enum E { + case e(T) +} + +// rdar://108738034 - Make sure we don't treat 'E' as a binding, but can treat +// 'y' as a binding +func testNonBinding1(_ x: E) -> Int { + if case let E.e(y) = x { y } else { 0 } +} + +func testNonBinding2(_ e: E) -> Int { + switch e { + case let E.e(y): + y + } +} + +// In this case, 'y' should be an identifier, but 'z' is a binding. +func testNonBinding3(_ x: (Int, Int), y: [Int]) -> Int { + if case let (y[0], z) = x { z } else { 0 } +} + +func testNonBinding4(_ x: (Int, Int), y: [Int]) -> Int { + switch x { + case let (y[0], z): + z + default: + 0 + } +} + +func testNonBinding5(_ x: Int, y: [Int]) { + // We treat 'z' here as a binding, which is invalid. + if case let y[z] = x {} // expected-error {{pattern variable binding cannot appear in an expression}} +} + +func testNonBinding6(y: [Int], z: Int) -> Int { + switch 0 { + // We treat 'z' here as a binding, which is invalid. + case let y[z]: // expected-error {{pattern variable binding cannot appear in an expression}} + z + case y[z]: // This is fine + 0 + default: + 0 + } +}