mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Detect and fix situations when (force) unwrap is used on a non-optional type, this helps to diagnose invalid unwraps precisely and provide fix-its. Resolves: [SR-8977](https://bugs.swift.org/browse/SR-8977) Resolves: rdar://problem/45218255
285 lines
8.2 KiB
Swift
285 lines
8.2 KiB
Swift
// RUN: %target-typecheck-verify-swift
|
|
|
|
// REQUIRES: objc_interop
|
|
import Foundation
|
|
|
|
struct S0 {
|
|
init!(int: Int) { }
|
|
init! (uint: UInt) { }
|
|
init !(float: Float) { }
|
|
|
|
init?(string: String) { }
|
|
init ?(double: Double) { }
|
|
init ? (char: Character) { }
|
|
}
|
|
|
|
struct S1<T> {
|
|
init?(value: T) { }
|
|
}
|
|
|
|
class DuplicateDecls {
|
|
init!() { } // expected-note{{'init()' previously declared here}}
|
|
init?() { } // expected-error{{invalid redeclaration of 'init()'}}
|
|
|
|
init!(string: String) { } // expected-note{{'init(string:)' previously declared here}}
|
|
init(string: String) { } // expected-error{{invalid redeclaration of 'init(string:)'}}
|
|
|
|
init(double: Double) { } // expected-note{{'init(double:)' previously declared here}}
|
|
init?(double: Double) { } // expected-error{{invalid redeclaration of 'init(double:)'}}
|
|
}
|
|
|
|
// Construct via a failable initializer.
|
|
func testConstruction(_ i: Int, s: String) {
|
|
let s0Opt = S0(string: s)
|
|
assert(s0Opt != nil)
|
|
var _: S0 = s0Opt // expected-error{{value of optional type 'S0?' must be unwrapped}}
|
|
// expected-note@-1{{coalesce}}
|
|
// expected-note@-2{{force-unwrap}}
|
|
|
|
let s0IUO = S0(int: i)
|
|
assert(s0IUO != nil)
|
|
|
|
_ = s0IUO
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Superclass initializer chaining
|
|
// ----------------------------------------------------------------------------
|
|
class Super {
|
|
init?(fail: String) { }
|
|
init!(failIUO: String) { }
|
|
init() { } // expected-note 2{{non-failable initializer 'init()' overridden here}}
|
|
}
|
|
|
|
class Sub : Super {
|
|
override init() { super.init() } // okay, never fails
|
|
|
|
init(nonfail: Int) { // expected-note{{propagate the failure with 'init?'}}{{7-7=?}}
|
|
super.init(fail: "boom") // expected-error{{a non-failable initializer cannot chain to failable initializer 'init(fail:)' written with 'init?'}}
|
|
// expected-note@-1{{force potentially-failing result with '!'}}{{29-29=!}}
|
|
}
|
|
|
|
convenience init(forceNonfail: Int) {
|
|
self.init(nonfail: forceNonfail)! // expected-error{{cannot force unwrap value of non-optional type 'Sub'}} {{37-38=}}
|
|
}
|
|
|
|
init(nonfail2: Int) { // okay, traps on nil
|
|
super.init(failIUO: "boom")
|
|
}
|
|
|
|
init(nonfail3: Int) {
|
|
super.init(fail: "boom")!
|
|
}
|
|
|
|
override init?(fail: String) {
|
|
super.init(fail: fail) // okay, propagates ?
|
|
}
|
|
|
|
init?(fail2: String) { // okay, propagates ! as ?
|
|
super.init(failIUO: fail2)
|
|
}
|
|
|
|
init?(fail3: String) { // okay, can introduce its own failure
|
|
super.init()
|
|
}
|
|
|
|
override init!(failIUO: String) {
|
|
super.init(failIUO: failIUO) // okay, propagates !
|
|
}
|
|
|
|
init!(failIUO2: String) { // okay, propagates ? as !
|
|
super.init(fail: failIUO2)
|
|
}
|
|
|
|
init!(failIUO3: String) { // okay, can introduce its own failure
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Initializer delegation
|
|
// ----------------------------------------------------------------------------
|
|
extension Super {
|
|
convenience init(convenienceNonFailNonFail: String) { // okay, non-failable
|
|
self.init()
|
|
}
|
|
|
|
convenience init(convenienceNonFailFail: String) { // expected-note{{propagate the failure with 'init?'}}{{19-19=?}}
|
|
self.init(fail: convenienceNonFailFail) // expected-error{{a non-failable initializer cannot delegate to failable initializer 'init(fail:)' written with 'init?'}}
|
|
// expected-note@-1{{force potentially-failing result with '!'}}{{44-44=!}}
|
|
}
|
|
|
|
convenience init(convenienceNonFailFailForce: String) {
|
|
self.init(fail: convenienceNonFailFailForce)!
|
|
}
|
|
|
|
convenience init(convenienceNonFailFailIUO: String) { // okay, trap on failure
|
|
self.init(failIUO: convenienceNonFailFailIUO)
|
|
}
|
|
|
|
convenience init?(convenienceFailNonFail: String) {
|
|
self.init() // okay, can introduce its own failure
|
|
}
|
|
|
|
convenience init?(convenienceFailFail: String) {
|
|
self.init(fail: convenienceFailFail) // okay, propagates ?
|
|
}
|
|
|
|
convenience init?(convenienceFailFailIUO: String) { // okay, propagates ! as ?
|
|
self.init(failIUO: convenienceFailFailIUO)
|
|
}
|
|
|
|
convenience init!(convenienceFailIUONonFail: String) {
|
|
self.init() // okay, can introduce its own failure
|
|
}
|
|
|
|
convenience init!(convenienceFailIUOFail: String) {
|
|
self.init(fail: convenienceFailIUOFail) // okay, propagates ? as !
|
|
}
|
|
|
|
convenience init!(convenienceFailIUOFailIUO: String) { // okay, propagates !
|
|
self.init(failIUO: convenienceFailIUOFailIUO)
|
|
}
|
|
}
|
|
|
|
struct SomeStruct {
|
|
init(nonFail: Int) { // expected-note{{propagate the failure with 'init?'}}{{8-8=?}}
|
|
self.init(fail: nonFail) // expected-error{{a non-failable initializer cannot delegate to failable initializer 'init(fail:)' written with 'init?'}}
|
|
// expected-note@-1{{force potentially-failing result with '!'}}{{29-29=!}}
|
|
}
|
|
|
|
init(nonFail2: Int) {
|
|
self.init(fail: nonFail2)!
|
|
}
|
|
|
|
init?(fail: Int) {}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Initializer overriding
|
|
// ----------------------------------------------------------------------------
|
|
class Sub2 : Super {
|
|
override init!(fail: String) { // okay to change ? to !
|
|
super.init(fail: fail)
|
|
}
|
|
override init?(failIUO: String) { // okay to change ! to ?
|
|
super.init(failIUO: failIUO)
|
|
}
|
|
|
|
override init() { super.init() } // no change
|
|
}
|
|
|
|
// Dropping optionality
|
|
class Sub3 : Super {
|
|
override init(fail: String) { // okay, strengthened result type
|
|
super.init()
|
|
}
|
|
|
|
override init(failIUO: String) { // okay, strengthened result type
|
|
super.init()
|
|
}
|
|
|
|
override init() { } // no change
|
|
}
|
|
|
|
// Adding optionality
|
|
class Sub4 : Super {
|
|
override init?(fail: String) { super.init() }
|
|
override init!(failIUO: String) { super.init() }
|
|
|
|
override init?() { // expected-error{{failable initializer 'init()' cannot override a non-failable initializer}}
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
class Sub5 : Super {
|
|
override init?(fail: String) { super.init() }
|
|
override init!(failIUO: String) { super.init() }
|
|
|
|
override init!() { // expected-error{{failable initializer 'init()' cannot override a non-failable initializer}}
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Initializer conformances
|
|
// ----------------------------------------------------------------------------
|
|
protocol P1 {
|
|
init(string: String)
|
|
}
|
|
|
|
@objc protocol P1_objc {
|
|
init(string: String)
|
|
}
|
|
|
|
protocol P2 {
|
|
init?(fail: String)
|
|
}
|
|
|
|
protocol P3 {
|
|
init!(failIUO: String)
|
|
}
|
|
|
|
class C1a : P1 {
|
|
required init?(string: String) { } // expected-error{{non-failable initializer requirement 'init(string:)' cannot be satisfied by a failable initializer ('init?')}}
|
|
}
|
|
|
|
class C1b : P1 {
|
|
required init!(string: String) { } // okay
|
|
}
|
|
|
|
class C1b_objc : P1_objc {
|
|
@objc required init!(string: String) { } // expected-error{{non-failable initializer requirement 'init(string:)' in Objective-C protocol cannot be satisfied by a failable initializer ('init!')}}
|
|
}
|
|
|
|
class C1c {
|
|
required init?(string: String) { } // expected-note {{'init(string:)' declared here}}
|
|
}
|
|
extension C1c: P1 {} // expected-error{{non-failable initializer requirement 'init(string:)' cannot be satisfied by a failable initializer ('init?')}}
|
|
|
|
class C2a : P2 {
|
|
required init(fail: String) { } // okay to remove failability
|
|
}
|
|
|
|
class C2b : P2 {
|
|
required init?(fail: String) { } // okay, ? matches
|
|
}
|
|
|
|
class C2c : P2 {
|
|
required init!(fail: String) { } // okay to satisfy init? with init!
|
|
}
|
|
|
|
class C3a : P3 {
|
|
required init(failIUO: String) { } // okay to remove failability
|
|
}
|
|
|
|
class C3b : P3 {
|
|
required init?(failIUO: String) { } // okay to satisfy ! with ?
|
|
}
|
|
|
|
class C3c : P3 {
|
|
required init!(failIUO: String) { } // okay, ! matches
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Initiating failure
|
|
// ----------------------------------------------------------------------------
|
|
struct InitiateFailureS {
|
|
init(string: String) { // expected-note{{use 'init?' to make the initializer 'init(string:)' failable}}{{7-7=?}}
|
|
return (nil) // expected-error{{only a failable initializer can return 'nil'}}
|
|
}
|
|
|
|
init(int: Int) {
|
|
return 0 // expected-error{{'nil' is the only return value permitted in an initializer}}
|
|
}
|
|
|
|
init?(double: Double) {
|
|
return nil // ok
|
|
}
|
|
|
|
init!(char: Character) {
|
|
return nil // ok
|
|
}
|
|
}
|