// RUN: %target-typecheck-verify-swift enum E { case e case f case g(Int) } func testDotSyntax1() -> E { switch Bool.random() { case true: .e case false: .f } } func testDotSyntax2() -> E? { switch Bool.random() { case true: .e case false: .f } } func testDotSyntax3() -> E? { switch Bool.random() { case true: .e case false: .none } } func testDotSyntax4() -> Int { let i = switch Bool.random() { case true: 0 case false: .random() } // expected-error@-1 {{cannot infer contextual base in reference to member 'random'}} return i } let testVar1: E = switch Bool.random() { case true: .e case false: .f } let testVar2: E? = switch Bool.random() { case true: .e case false: .f } let testVar3: E? = switch Bool.random() { case true: .e case false: .none } let testVar4: E? = switch Bool.random() { case true: nil case false: .e } let testVar5 = switch Bool.random() { case true: 0 case false: 1.0 } // expected-error@-1 {{branches have mismatching types 'Int' and 'Double'}} let testVar6: Double = switch Bool.random() { case true: 0 case false: 1.0 } let testVar7: Double = switch 0 { case 1: 0 + 1 case 2: 1.0 + 3 default: 9 + 0.0 } let testContextualMismatch1: String = switch Bool.random() { case true: 1 case false: "" } // expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}} let testContextualMismatch2: String = switch Bool.random() { case true: 1 case false: 2 } // expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}} func proposalExample1(_ x: Int) -> Float { let y = switch x { case 0..<0x80: 1 // expected-error {{branches have mismatching types 'Int' and 'Double'}} case 0x80..<0x0800: 2.0 case 0x0800..<0x1_0000: 3.0 default: 4.5 } return y } func proposalExample2(_ x: Int) -> Float { let y: Float = switch x { case 0..<0x80: 1 case 0x80..<0x0800: 2.0 case 0x0800..<0x1_0000: 3.0 default: 4.5 } return y } enum Node { case B, R } enum Tree { indirect case node(Node, Tree, Tree, Tree) case leaf func proposalExample3(_ z: Tree, d: Tree) -> Tree { switch self { case let .node(.B, .node(.R, .node(.R, a, x, b), y, c), z, d): .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) case let .node(.B, .node(.R, a, x, .node(.R, b, y, c)), z, d): .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) case let .node(.B, a, x, .node(.R, .node(.R, b, y, c), z, d)): .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) case let .node(.B, a, x, .node(.R, b, y, .node(.R, c, z, d))): .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) default: self } } } enum F { case a(Int) } func overloadedWithGenericAndInt(_ x: T) -> T { x } func overloadedWithGenericAndInt(_ x: Int) -> Int { x } struct S { var f: F mutating func foo() -> Int { switch f { case .a(let x): // Make sure we don't try and shrink, which would lead to trying to // type-check the switch early. overloadedWithGenericAndInt(x + x) } } } func testSingleCaseReturn(_ f: F) -> Int { switch f { case .a(let i): i } } func testSingleCaseReturnClosure(_ f: F) -> Int { let fn = { switch f { case .a(let i): i } } return fn() } func testWhereClause(_ f: F) -> Int { switch f { case let .a(x) where x.isMultiple(of: 2): return 0 default: return 1 } } func testNestedOptional() -> Int? { switch Bool.random() { case true: 1 case false: if .random() { 1 } else { nil } } } func testNestedOptionalSwitch() -> Int? { switch Bool.random() { case true: 1 case false: switch Bool.random() { case true: 1 case false: nil } } } func testNestedOptionalMismatch1() -> Int? { switch Bool.random() { case true: 1 case false: if .random() { 1 } else { "" // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} } } } func testNestedOptionalMismatch2() -> Int { switch Bool.random() { case true: 1 case false: if .random() { 1 } else { // FIXME: Seems like we could do better here nil // expected-error {{cannot convert value of type 'ExpressibleByNilLiteral' to specified type 'Int'}} } } } func testAssignment() { var d: Double = switch Bool.random() { case true: 0 case false: 1.0 } d = switch Bool.random() { case true: 0 case false: 1 } _ = d } struct TestBadReturn { var y = switch Bool.random() { case true: return case false: 0 } // expected-error@-1 {{return invalid outside of a func}} // expected-error@-2 {{cannot use 'return' to transfer control out of 'switch' expression}} } func testNil1(_ x: Bool) { let _ = switch x { case true: 42 case false: nil } // expected-error {{'nil' requires a contextual type}} } func testNil2(_ x: Bool) { let _ = switch x { case true: nil case false: 42 } // expected-error {{'nil' requires a contextual type}} } func testNil3(_ x: Bool) { // In this case, we allow propagating the type from the first branch into // later branches. let _: _? = switch x { case true: 42 case false: nil } } func testNil4(_ x: Bool) { let _: _? = switch x { case true: nil case false: 42 } // expected-error {{could not infer type for placeholder}} } enum G { // expected-note@-1 {{arguments to generic parameter 'T' ('Double' and 'Int') are expected to be equal}} // expected-note@-2 {{'T' declared as parameter to type 'G'}} case x(T), y } func testUnboundGeneric1() -> G { let x: G = switch Bool.random() { case true: .x(5) case false: .x(1) } return x } func testUnboundGeneric2() -> G { let x: G = switch Bool.random() { case true: .x(5) case false: .y } return x } func testUnboundGeneric3() -> G { let x: G = switch Bool.random() { case true: .y case false: .y } // expected-error@-1 {{generic parameter 'T' could not be inferred}} return x } func testUnboundGeneric4() -> G { let x: G = switch Bool.random() { case true: .x(5.0) case false: .x(5.0) } return x // expected-error@-1 {{cannot convert return expression of type 'G' to return type 'G'}} } func testUnboundGeneric5() -> G { let x: G = switch Bool.random() { case true: .x(5) case false: .x(5.0) } // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} return x } func testNeverConversion1() -> Int { switch Bool.random() { case true: 1 case false: fatalError() } } func testNeverConversion2() -> Int { return switch Bool.random() { case true: 1 case false: fatalError() } } func testNeverConversion3() -> Int { switch Bool.random() { case true: 1 case false: if .random() { fatalError() } else { 2 } } } func testNeverConversion4() -> Int { return switch Bool.random() { case true: 1 case false: if .random() { fatalError() } else { 2 } } } func testNeverConversion5() -> Int { { switch Bool.random() { case true: 1 case false: if .random() { fatalError() } else { 2 } } }() } func testNeverConversion6(_ e: E) -> String { switch e { default: fatalError() } } func testVoidConversion() { func foo(_ fn: () -> Void) {} func bar(_ fn: () -> T) {} // Okay for an implicit return, including nested as this preserves source // compatibility. foo { switch Bool.random() { case true: 0 // expected-warning {{integer literal is unused}} case false: 0 // expected-warning {{integer literal is unused}} } } foo { switch Bool.random() { case true: 0 // expected-warning {{integer literal is unused}} case false: if .random() { 0 // expected-warning {{integer literal is unused}} } else { 0 // expected-warning {{integer literal is unused}} } } } foo { switch Bool.random() { default: 0 // expected-warning {{integer literal is unused}} } } bar { switch Bool.random() { case true: 0 case false: 0 } } bar { switch Bool.random() { case true: 0 case false: if .random() { 0 } else { 0 } } } bar { switch Bool.random() { case true: () case false: 0 // expected-warning {{integer literal is unused}} } } bar { switch Bool.random() { case true: 0 // expected-warning {{integer literal is unused}} case false: if .random() { () } else { 0 // expected-warning {{integer literal is unused}} } } } // We allow the branches to mismatch to preserve source compatibility. // (this example is silly, but this occurs in the wild for more innocuous // things like branches that do set insertions and removals). bar { switch Bool.random() { case true: 0 case false: "" } // expected-warning@-1 {{integer literal is unused}} // expected-warning@-2 {{string literal is unused}} } bar { switch Bool.random() { case true: switch Bool.random() { case true: 0 // expected-warning {{integer literal is unused}} case false: [0] // expected-warning {{expression of type '[Int]' is unused}} } case false: "" // expected-warning {{string literal is unused}} } } bar { () -> Void in switch Bool.random() { case true: 0 case false: "" } // expected-warning@-1 {{integer literal is unused}} // expected-warning@-2 {{string literal is unused}} } bar { () -> Void in switch Bool.random() { case true: switch Bool.random() { case true: 0 // expected-warning {{integer literal is unused}} case false: [0] // expected-warning {{expression of type '[Int]' is unused}} } case false: "" // expected-warning {{string literal is unused}} } } bar { () -> Int in switch Bool.random() { case true: 0 case false: "" } // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} } bar { () -> Int in switch Bool.random() { case true: switch Bool.random() { case true: 0 case false: [0] // expected-error {{cannot convert value of type '[Int]' to specified type 'Int'}} } case false: "" } } // Not okay for an explicit return. foo { return switch Bool.random() { case true: 0 // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}} case false: 0 } } foo { return switch Bool.random() { case true: 0 // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}} case false: if .random() { 0 } else { 0 } } } bar { return switch Bool.random() { case true: 0 case false: 0 } } bar { return switch Bool.random() { case true: 0 case false: if .random() { 0 } else { 0 } } } bar { return switch Bool.random() { case true: () // expected-error {{branches have mismatching types '()' and 'Int'}} case false: 0 } } bar { return switch Bool.random() { case true: 0 case false: if .random() { () // expected-error {{branches have mismatching types '()' and 'Int'}} } else { 0 } } } bar { return switch Bool.random() { case true: 0 case false: "" } // expected-error {{branches have mismatching types 'Int' and 'String'}} } bar { return switch Bool.random() { case true: switch Bool.random() { case true: 0 // expected-error {{branches have mismatching types 'Int' and '[Int]'}} case false: [0] } case false: "" } } } struct SomeError: Error {} func testThrowInference() { func notThrowing(_ fn: () -> Int) {} notThrowing { // expected-error {{invalid conversion from throwing function of type '() throws -> Int' to non-throwing function type '() -> Int'}} switch Bool.random() { case true: 0 case false: throw SomeError() } } @discardableResult func rethrowing(_ fn: () throws -> T) rethrows -> T { try fn() } rethrowing { // expected-error@-1 {{call can throw, but it is not marked with 'try' and the error is not handled}} // expected-note@-2 {{call is to 'rethrows' function, but argument function can throw}} switch Bool.random() { case true: 0 case false: throw SomeError() } } } // MARK: Pound if func testPoundIf1() -> Int { switch Bool.random() { case true: #if true 0 #else "" #endif case false: 0 } } func testPoundIf2() -> String { switch Bool.random() { case true: #if true 0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}} #else "" #endif case false: "" } } func testPoundIf3() -> String { switch Bool.random() { case true: #if false 0 #else "" #endif case false: "" } } func testPoundIf4() -> String { let x = switch Bool.random() { case true: #if true 0 // expected-error {{branches have mismatching types 'Int' and 'String'}} #else "" #endif case false: "" } return x } func testPoundIf5() -> String { let x = switch Bool.random() { case true: #if false 0 #else "" #endif case false: "" } return x } func testPoundIfClosure1() -> Int { let fn = { switch Bool.random() { case true: #if true 0 #else "" #endif case false: 0 } } return fn() } func testPoundIfClosure2() -> String { let fn: () -> String = { switch Bool.random() { case true: #if true 0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}} #else "" #endif case false: "" } } return fn() } // MARK: Subtyping class A {} class B : A {} class C : A {} func testSubtyping1() -> A { // We can join to A. let x = switch Bool.random() { case true: B() case false: C() } let y = .random() ? B() : C() if .random() { return x } else { return y } } // MARK: Opaque result types protocol P {} extension P { @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) func foo() -> some P { 0 } } extension Int : P {} extension Never : P {} @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) func testOpaqueReturn1() -> some P { switch Bool.random() { case true: 0 case false: 1 } } @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) func testOpaqueReturn2() -> some P { switch Bool.random() { case true: 0 case false: fatalError() } } @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) func testOpaqueReturn3(_ x: Int) -> some P { switch Bool.random() { case true: return x.foo() case false: fatalError() } } // MARK: Result builders enum Either { case first(T), second(U) } @resultBuilder struct Builder { static func buildBlock(_ x: T) -> T { x } static func buildBlock(_ x: T, _ y: U) -> (T, U) { (x, y) } static func buildEither(first x: T) -> Either { .first(x) } static func buildEither(second x: U) -> Either { .second(x) } static func buildExpression(_ x: Double) -> Double { x } static func buildExpression(_ x: T) -> T { x } } @Builder func singleExprBuilder() -> Either { switch Bool.random() { case true: "" case false: 1 } } @Builder func builderStaticMember() -> (Either, Double) { switch Bool.random() { case true: "" case false: 1 } .pi // This becomes a static member ref, not a member on an if expression. } @Builder func builderNotPostfix() -> (Either, Bool) { switch Bool.random() { case true: "" case false: 1 } !.random() // expected-error {{consecutive statements on a line must be separated by ';'}} } @Builder func builderWithBinding() -> Either { // Make sure the binding gets type-checked as a switch expression, but the // other if block gets type-checked as a stmt. let str = switch Bool.random() { case true: "a" case false: "b" } if .random() { str } else { 1 } } @Builder func builderWithInvalidBinding() -> Either { let str = (switch Bool.random() { default: "a" }) // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} if .random() { str } else { 1 } } func takesBuilder(@Builder _ fn: () -> Either) {} func builderClosureWithBinding() { takesBuilder { // Make sure the binding gets type-checked as a switch expression, but the // other if block gets type-checked as a stmt. let str = switch Bool.random() { case true: "a" case false: "b" } switch Bool.random() { case true: str case false: 1 } } } func builderClosureWithInvalidBinding() { takesBuilder { let str = (switch Bool.random() { case true: "a" case false: "b" }) // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} switch Bool.random() { case true: str case false: 1 } } } func builderInClosure() { takesBuilder { switch Bool.random() { case true: "" case false: 1 } } }