mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Since we have `repairViaOptionalUnwrap`, let's use that to fix argument/parameter mismatches instead of doing ad-hoc matching.
732 lines
15 KiB
Swift
732 lines
15 KiB
Swift
// RUN: %target-typecheck-verify-swift
|
|
|
|
enum E {
|
|
case e
|
|
case f
|
|
case g(Int)
|
|
}
|
|
|
|
func testDotSyntax1() -> E {
|
|
if .random() { .e } else { .f }
|
|
}
|
|
func testDotSyntax2() -> E? {
|
|
if .random() { .e } else { .f }
|
|
}
|
|
func testDotSyntax3() -> E? {
|
|
if .random() { .e } else { .none }
|
|
}
|
|
func testDotSyntax4() -> Int {
|
|
let i = if .random() { 0 } else { .random() }
|
|
// expected-error@-1 {{cannot infer contextual base in reference to member 'random'}}
|
|
|
|
return i
|
|
}
|
|
|
|
let testVar1: E = if .random() { .e } else { .f }
|
|
let testVar2: E? = if .random() { .e } else { .f }
|
|
let testVar3: E? = if .random() { .e } else { .none }
|
|
let testVar4: E? = if .random() { nil } else { .e }
|
|
|
|
let testVar5 = if .random() { 0 } else { 1.0 }
|
|
// expected-error@-1 {{branches have mismatching types 'Int' and 'Double'}}
|
|
|
|
let testVar6: Double = if .random() { 0 } else { 1.0 }
|
|
|
|
let testVar7: Double = if .random() { 0 + 1 } else if .random() { 1.0 + 3 } else { 9 + 0.0 }
|
|
|
|
let testContextualMismatch1: String = if .random() { 1 } else { "" }
|
|
// expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}}
|
|
|
|
let testContextualMismatch2: String = if .random() { 1 } else { 2 }
|
|
// expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}}
|
|
|
|
let testMismatch1 = if .random() { 1 } else if .random() { "" } else { 0.0 }
|
|
// expected-error@-1 {{branches have mismatching types 'Int' and 'Double'}}
|
|
// expected-error@-2 {{branches have mismatching types 'String' and 'Double'}}
|
|
|
|
func testMismatch2() -> Double {
|
|
let x = if .random() {
|
|
0 // expected-error {{branches have mismatching types 'Int' and 'Double'}}
|
|
} else if .random() {
|
|
0
|
|
} else {
|
|
1.0
|
|
}
|
|
return x
|
|
}
|
|
func testOptionalBinding1(_ x: Int?) -> Int {
|
|
if let x = x { x } else { 0 }
|
|
}
|
|
func testOptionalBinding2(_ x: Int?, _ y: Int?) -> Int {
|
|
if let x = x, let y = y { x + y } else { 0 }
|
|
}
|
|
func testPatternBinding2(_ e: E) -> Int {
|
|
if case .g(let i) = e { i } else { 0 }
|
|
}
|
|
|
|
func testTernary() -> Int {
|
|
if .random() { .random() ? 1 : 0 } else { .random() ? 3 : 2 }
|
|
}
|
|
|
|
func testReturn() -> Int {
|
|
if .random() {
|
|
return 0
|
|
} else {
|
|
1 // expected-warning {{integer literal is unused}}
|
|
}
|
|
}
|
|
|
|
func testNil1(_ x: Bool) {
|
|
let _ = if x { 42 } else { nil } // expected-error {{'nil' requires a contextual type}}
|
|
}
|
|
func testNil2(_ x: Bool) {
|
|
let _ = if x { nil } else { 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 _: _? = if x { 42 } else { nil }
|
|
}
|
|
func testNil4(_ x: Bool) {
|
|
let _: _? = if x { nil } else { 42 } // expected-error {{could not infer type for placeholder}}
|
|
}
|
|
|
|
enum F<T> {
|
|
// 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 'F'}}
|
|
case x(T), y
|
|
}
|
|
|
|
func testUnboundGeneric1() -> F<Int> {
|
|
let x: F = if .random() { .x(5) } else { .x(1) }
|
|
return x
|
|
}
|
|
|
|
func testUnboundGeneric2() -> F<Int> {
|
|
let x: F = if .random() { .x(5) } else { .y }
|
|
return x
|
|
}
|
|
|
|
func testUnboundGeneric3() -> F<Int> {
|
|
let x: F = if .random() { .y } else { .y }
|
|
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
|
|
return x
|
|
}
|
|
|
|
func testUnboundGeneric4() -> F<Int> {
|
|
let x: F = if .random() { .x(5.0) } else { .x(5.0) }
|
|
return x
|
|
// expected-error@-1 {{cannot convert return expression of type 'F<Double>' to return type 'F<Int>'}}
|
|
}
|
|
|
|
func testUnboundGeneric5() -> F<Int> {
|
|
let x: F = if .random() { .x(5) } else { .x(5.0) }
|
|
// expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}}
|
|
return x
|
|
}
|
|
|
|
protocol Q {
|
|
associatedtype X
|
|
}
|
|
|
|
struct SQ : Q {
|
|
typealias X = String
|
|
}
|
|
|
|
func testAssociatedTypeReturn1() {
|
|
func fn<T : Q>(_ fn: (T) -> T.X) {}
|
|
fn { x in // expected-error {{cannot infer type of closure parameter 'x' without a type annotation}}
|
|
if .random() { "" } else { "" }
|
|
}
|
|
fn { (x: SQ) in
|
|
if .random() { "" } else { "" }
|
|
}
|
|
fn { (x: SQ) in
|
|
if .random() { "" } else { 0 } // expected-error {{cannot convert value of type 'Int' to specified type 'SQ.X' (aka 'String')}}
|
|
}
|
|
}
|
|
|
|
func testNeverConversion1() -> Int {
|
|
if .random() {
|
|
1
|
|
} else {
|
|
fatalError()
|
|
}
|
|
}
|
|
|
|
func testNeverConversion2() -> Int {
|
|
return if .random() {
|
|
1
|
|
} else {
|
|
fatalError()
|
|
}
|
|
}
|
|
|
|
func testNeverConversion3() -> Int {
|
|
if .random() {
|
|
1
|
|
} else {
|
|
if .random() {
|
|
fatalError()
|
|
} else {
|
|
2
|
|
}
|
|
}
|
|
}
|
|
|
|
func testNeverConversion4() -> Int {
|
|
return if .random() {
|
|
1
|
|
} else {
|
|
if .random() {
|
|
fatalError()
|
|
} else {
|
|
2
|
|
}
|
|
}
|
|
}
|
|
|
|
func testNeverConversion5() -> Int {
|
|
{
|
|
if .random() {
|
|
1
|
|
} else {
|
|
if .random() {
|
|
fatalError()
|
|
} else {
|
|
2
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func testVoidConversion() {
|
|
func foo(_ fn: () -> Void) {}
|
|
func bar<T>(_ fn: () -> T) {}
|
|
|
|
// Okay for an implicit return, including nested as this preserves source
|
|
// compatibility.
|
|
foo {
|
|
if .random() {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
} else {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
}
|
|
}
|
|
foo {
|
|
if .random() {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
} else {
|
|
if .random() {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
} else {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
}
|
|
}
|
|
}
|
|
bar {
|
|
if .random() {
|
|
0
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
bar {
|
|
if .random() {
|
|
0
|
|
} else {
|
|
if .random() {
|
|
0
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
}
|
|
bar {
|
|
if .random() {
|
|
()
|
|
} else {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
}
|
|
}
|
|
bar {
|
|
if .random() {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
} else {
|
|
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 {
|
|
if .random() { 0 } else { "" }
|
|
// expected-warning@-1 {{integer literal is unused}}
|
|
// expected-warning@-2 {{string literal is unused}}
|
|
}
|
|
bar {
|
|
if .random() {
|
|
if .random() {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
} else {
|
|
[0] // expected-warning {{expression of type '[Int]' is unused}}
|
|
}
|
|
} else {
|
|
"" // expected-warning {{string literal is unused}}
|
|
}
|
|
}
|
|
bar { () -> Void in
|
|
if .random() { 0 } else { "" }
|
|
// expected-warning@-1 {{integer literal is unused}}
|
|
// expected-warning@-2 {{string literal is unused}}
|
|
}
|
|
bar { () -> Void in
|
|
if .random() {
|
|
if .random() {
|
|
0 // expected-warning {{integer literal is unused}}
|
|
} else {
|
|
[0] // expected-warning {{expression of type '[Int]' is unused}}
|
|
}
|
|
} else {
|
|
"" // expected-warning {{string literal is unused}}
|
|
}
|
|
}
|
|
bar { () -> Int in
|
|
if .random() { 0 } else { "" } // expected-error {{cannot convert value of type 'String' to specified type 'Int'}}
|
|
}
|
|
bar { () -> Int in
|
|
if .random() {
|
|
if .random() {
|
|
0
|
|
} else {
|
|
[0] // expected-error {{cannot convert value of type '[Int]' to specified type 'Int'}}
|
|
}
|
|
} else {
|
|
""
|
|
}
|
|
}
|
|
|
|
// Not okay for an explicit return.
|
|
foo {
|
|
return if .random() {
|
|
0 // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}}
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
foo {
|
|
return if .random() {
|
|
0 // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}}
|
|
} else {
|
|
if .random() {
|
|
0
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
}
|
|
bar {
|
|
return if .random() {
|
|
0
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
bar {
|
|
return if .random() {
|
|
0
|
|
} else {
|
|
if .random() {
|
|
0
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
}
|
|
bar {
|
|
return if .random() {
|
|
() // expected-error {{branches have mismatching types '()' and 'Int'}}
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
bar {
|
|
return if .random() {
|
|
0
|
|
} else {
|
|
if .random() {
|
|
() // expected-error {{branches have mismatching types '()' and 'Int'}}
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
}
|
|
bar {
|
|
return if .random() { 0 } else { "" } // expected-error {{branches have mismatching types 'Int' and 'String'}}
|
|
}
|
|
bar {
|
|
return if .random() {
|
|
if .random() {
|
|
0 // expected-error {{branches have mismatching types 'Int' and '[Int]'}}
|
|
} else {
|
|
[0]
|
|
}
|
|
} else {
|
|
""
|
|
}
|
|
}
|
|
}
|
|
|
|
func testReturnMismatch() {
|
|
let _ = if .random() {
|
|
return 1
|
|
// expected-error@-1 {{cannot use 'return' to transfer control out of 'if' expression}}
|
|
// expected-error@-2 {{unexpected non-void return value in void function}}
|
|
// expected-note@-3 {{did you mean to add a return type?}}
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
func testOptionalGeneric() {
|
|
func bar<T>(_ fn: () -> T?) -> T? { fn() }
|
|
bar {
|
|
if .random() {
|
|
()
|
|
} else {
|
|
()
|
|
}
|
|
}
|
|
}
|
|
|
|
func testNestedOptional() -> Int? {
|
|
if .random() {
|
|
1
|
|
} else {
|
|
if .random() {
|
|
1
|
|
} else {
|
|
nil
|
|
}
|
|
}
|
|
}
|
|
|
|
let neverVar = if .random() { fatalError() } else { fatalError() }
|
|
// expected-warning@-1 {{constant 'neverVar' inferred to have type 'Never'}}
|
|
// expected-note@-2 {{add an explicit type annotation to silence this warning}}
|
|
|
|
func testNonVoidToVoid() {
|
|
if .random() { 0 } else { 1 } // expected-warning 2{{integer literal is unused}}
|
|
}
|
|
|
|
func uninferableNil() {
|
|
let _ = if .random() { nil } else { 2.0 } // expected-error {{'nil' requires a contextual type}}
|
|
}
|
|
|
|
func testAssignment() {
|
|
var d: Double = if .random() { 0 } else { 1.0 }
|
|
d = if .random() { 0 } else { 1 }
|
|
_ = d
|
|
}
|
|
|
|
struct TestBadReturn {
|
|
var y = if .random() { return } else { 0 }
|
|
// expected-error@-1 {{return invalid outside of a func}}
|
|
// expected-error@-2 {{cannot use 'return' to transfer control out of 'if' expression}}
|
|
}
|
|
|
|
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'}}
|
|
if .random() {
|
|
0
|
|
} else {
|
|
throw SomeError()
|
|
}
|
|
}
|
|
|
|
@discardableResult
|
|
func rethrowing<T>(_ 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}}
|
|
if .random() {
|
|
0
|
|
} else {
|
|
throw SomeError()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Pound if
|
|
|
|
func testPoundIf1() -> Int {
|
|
if .random() {
|
|
#if true
|
|
0
|
|
#else
|
|
""
|
|
#endif
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
func testPoundIf2() -> String {
|
|
if .random() {
|
|
#if true
|
|
0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
|
|
#else
|
|
""
|
|
#endif
|
|
} else {
|
|
""
|
|
}
|
|
}
|
|
|
|
func testPoundIf3() -> String {
|
|
if .random() {
|
|
#if false
|
|
0
|
|
#else
|
|
""
|
|
#endif
|
|
} else {
|
|
""
|
|
}
|
|
}
|
|
|
|
func testPoundIf4() -> String {
|
|
let x = if .random() {
|
|
#if true
|
|
0 // expected-error {{branches have mismatching types 'Int' and 'String'}}
|
|
#else
|
|
""
|
|
#endif
|
|
} else {
|
|
""
|
|
}
|
|
return x
|
|
}
|
|
|
|
func testPoundIf5() -> String {
|
|
let x = if .random() {
|
|
#if false
|
|
0
|
|
#else
|
|
""
|
|
#endif
|
|
} else {
|
|
""
|
|
}
|
|
return x
|
|
}
|
|
|
|
func testPoundIfClosure1() -> Int {
|
|
let fn = {
|
|
if .random() {
|
|
#if true
|
|
0
|
|
#else
|
|
""
|
|
#endif
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
return fn()
|
|
}
|
|
|
|
func testPoundIfClosure2() -> String {
|
|
let fn: () -> String = {
|
|
if .random() {
|
|
#if true
|
|
0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
|
|
#else
|
|
""
|
|
#endif
|
|
} else {
|
|
""
|
|
}
|
|
}
|
|
return fn()
|
|
}
|
|
|
|
// MARK: Subtyping
|
|
|
|
class A {}
|
|
class B : A {}
|
|
class C : A {}
|
|
|
|
func testSubtyping1() -> A {
|
|
// We can join to A.
|
|
let x = if .random() { B() } else { C() }
|
|
let y = .random() ? B() : C()
|
|
if .random() {
|
|
return x
|
|
} else {
|
|
return y
|
|
}
|
|
}
|
|
|
|
// MARK: Opaque result types
|
|
|
|
protocol P {}
|
|
extension Int : P {}
|
|
|
|
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
|
func testOpaqueReturn1() -> some P {
|
|
if .random() {
|
|
0
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
|
|
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
|
func testOpaqueReturn2() -> some P {
|
|
if .random() {
|
|
0
|
|
} else {
|
|
fatalError()
|
|
}
|
|
}
|
|
|
|
// MARK: Result builders
|
|
|
|
enum Either<T, U> {
|
|
case first(T), second(U)
|
|
}
|
|
|
|
@resultBuilder
|
|
struct Builder {
|
|
static func buildBlock<T>(_ x: T) -> T { x }
|
|
static func buildBlock<T, U>(_ x: T, _ y: U) -> (T, U) { (x, y) }
|
|
|
|
static func buildEither<T, U>(first x: T) -> Either<T, U> { .first(x) }
|
|
static func buildEither<T, U>(second x: U) -> Either<T, U> { .second(x) }
|
|
|
|
static func buildExpression(_ x: Double) -> Double { x }
|
|
static func buildExpression<T>(_ x: T) -> T { x }
|
|
}
|
|
|
|
@Builder
|
|
func singleExprBuilder() -> Either<String, Int> {
|
|
if .random() {
|
|
""
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
|
|
@Builder
|
|
func builderStaticMember() -> (Either<String, Int>, Double) {
|
|
if .random() {
|
|
""
|
|
} else {
|
|
1
|
|
}
|
|
.pi // This becomes a static member ref, not a member on an if expression.
|
|
}
|
|
|
|
@Builder
|
|
func builderNotPostfix() -> (Either<String, Int>, Bool) {
|
|
if .random() { "" } else { 1 } !.random() // expected-error {{consecutive statements on a line must be separated by ';'}}
|
|
}
|
|
|
|
@Builder
|
|
func builderWithBinding() -> Either<String, Int> {
|
|
// Make sure the binding gets type-checked as an if expression, but the
|
|
// other if block gets type-checked as a stmt.
|
|
let str = if .random() { "a" } else { "b" }
|
|
if .random() {
|
|
str
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
|
|
@Builder
|
|
func builderWithInvalidBinding() -> Either<String, Int> {
|
|
let str = (if .random() { "a" } else { "b" })
|
|
// expected-error@-1 {{'if' 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<String, Int>) {}
|
|
|
|
func builderClosureWithBinding() {
|
|
takesBuilder {
|
|
// Make sure the binding gets type-checked as an if expression, but the
|
|
// other if block gets type-checked as a stmt.
|
|
let str = if .random() { "a" } else { "b" }
|
|
if .random() {
|
|
str
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
}
|
|
|
|
func builderClosureWithInvalidBinding() {
|
|
takesBuilder {
|
|
let str = (if .random() { "a" } else { "b" })
|
|
// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
|
if .random() {
|
|
str
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
}
|
|
|
|
func builderInClosure() {
|
|
takesBuilder {
|
|
if .random() {
|
|
""
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
}
|
|
|
|
// https://github.com/apple/swift/issues/63796
|
|
func testInvalidOptionalChainingInIfContext() {
|
|
let v63796 = 1
|
|
if v63796? {} // expected-error{{cannot use optional chaining on non-optional value of type 'Int'}}
|
|
// expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
|
|
}
|
|
|
|
// https://github.com/swiftlang/swift/issues/79395
|
|
_ = {
|
|
if .random() {
|
|
struct S: Error {}
|
|
throw S()
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
_ = {
|
|
if .random() {
|
|
if .random() {
|
|
struct S: Error {}
|
|
throw S()
|
|
} else {
|
|
0
|
|
}
|
|
} else {
|
|
1
|
|
}
|
|
}
|