Files
swift-mirror/test/decl/init/failable.swift
Anthony Latsis d113ca6bd2 CSApply: Fix several issues with non-failable to failable/throwing initializer delegation/chaining diagnostics
* Delegated-to `Optional` ctors were always handled as if they were failable, resulting in false-positive delegation errors.
* Delegations via `try?` were not diagnosed if the called ctor had IUO failability on top of being `throws`. SILGen would then handle the `try?` as if it was a `try!`.
* Delegations via `try?` were diagnosed with the wrong message if the `try?` was nested inside a `try!`, e.g. `try! try self.init(nonFailable:)`
* If there are issues with both `try?` and failability of the called initializer, diagnose both.
2022-06-01 00:11:45 +03:00

388 lines
13 KiB
Swift

// Run in compatibility modes that disable and enable optional flattening
// in 'try?' to verify that initializer delegation diagnostics that are related
// to 'try?' are stable.
// RUN: %target-typecheck-verify-swift -swift-version 4
// RUN: %target-typecheck-verify-swift -swift-version 5
// 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}} {{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?(failable: Void) {}
init!(failableIUO: Void) {}
init(throws: Void) throws {}
init?(failableAndThrows: Void) throws {}
init!(failableIUOAndThrows: Void) throws {}
init(delegationOk1: Void) {
self.init(failable: ())!
}
init(delegationOk2: Void) {
try! self.init(throws: ())
}
init(delegationOk3: Void) {
try! self.init(failableAndThrows: ())!
}
init(delegationOk4: Void) {
try! self.init(failableIUOAndThrows: ())
}
init(nonFailable: Void) { // expected-note{{propagate the failure with 'init?'}}{{7-7=?}}
self.init(failable: ()) // expected-error{{a non-failable initializer cannot delegate to failable initializer 'init(failable:)' written with 'init?'}}
// expected-note@-1{{force potentially-failing result with '!'}}{{28-28=!}}
}
init(delegationBad1: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
try? self.init(nonFailable: ())
// expected-warning@-1 {{no calls to throwing functions occur within 'try' expression}}
// expected-error@-2 {{a non-failable initializer cannot use 'try?' to delegate to another initializer}}
// expected-note@-3 {{force potentially-failing result with 'try!'}}{{5-9=try!}}
}
init(delegationBad2: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
try? self.init(failableIUO: ())
// expected-warning@-1 {{no calls to throwing functions occur within 'try' expression}}
// expected-error@-2 {{a non-failable initializer cannot use 'try?' to delegate to another initializer}}
// expected-note@-3 {{force potentially-failing result with 'try!'}}{{5-9=try!}}
}
init(delegationBad3: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
try? self.init(throws: ())
// expected-error@-1 {{a non-failable initializer cannot use 'try?' to delegate to another initializer}}
// expected-note@-2 {{force potentially-failing result with 'try!'}}{{5-9=try!}}
}
init(delegationBad4: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
try! try? self.init(throws: ())
// expected-warning@-1 {{no calls to throwing functions occur within 'try' expression}}
// expected-error@-2 {{a non-failable initializer cannot use 'try?' to delegate to another initializer}}
// expected-note@-3 {{force potentially-failing result with 'try!'}}{{10-14=try!}}
}
init(delegationBad5: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
try try? self.init(throws: ())
// expected-warning@-1 {{no calls to throwing functions occur within 'try' expression}}
// expected-error@-2 {{a non-failable initializer cannot use 'try?' to delegate to another initializer}}
// expected-note@-3 {{force potentially-failing result with 'try!'}}{{9-13=try!}}
}
init(delegationBad6: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
try? self.init(failableAndThrows: ())!
// expected-error@-1 {{a non-failable initializer cannot use 'try?' to delegate to another initializer}}
// expected-note@-2 {{force potentially-failing result with 'try!'}}{{5-9=try!}}
}
init(delegationBad7: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
try! self.init(failableAndThrows: ())
// expected-error@-1 {{a non-failable initializer cannot delegate to failable initializer 'init(failableAndThrows:)' written with 'init?'}}
// expected-note@-2 {{force potentially-failing result with '!'}}{{42-42=!}}
}
init(delegationBad8: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
try? self.init(failableIUOAndThrows: ())
// expected-error@-1 {{a non-failable initializer cannot use 'try?' to delegate to another initializer}}
// expected-note@-2 {{force potentially-failing result with 'try!'}}{{5-9=try!}}
}
init(delegationBad9: Void) { // expected-note 2 {{propagate the failure with 'init?'}}{{7-7=?}}
try? self.init(failableAndThrows: ())
// expected-error@-1 {{a non-failable initializer cannot use 'try?' to delegate to another initializer}}
// expected-error@-2 {{a non-failable initializer cannot delegate to failable initializer 'init(failableAndThrows:)' written with 'init?'}}
// expected-note@-3 {{force potentially-failing result with '!'}}{{42-42=!}}
// expected-note@-4 {{force potentially-failing result with 'try!'}}{{5-9=try!}}
}
}
extension Optional {
init(delegationOk1: Void) {
self.init(nonFailable: ())
}
init?(delegationOk2: Void) {
self.init(nonFailable: ())
}
init?(delegationOk3: Void) {
self.init(failable: ())
}
init(delegationBad1: Void) { // expected-note {{propagate the failure with 'init?'}}{{7-7=?}}
self.init(failable: ())
// expected-error@-1 {{a non-failable initializer cannot delegate to failable initializer 'init(failable:)' written with 'init?'}}
// expected-note@-2 {{force potentially-failing result with '!'}}{{28-28=!}}
}
init(nonFailable: Void) {}
init?(failable: Void) {}
}
// ----------------------------------------------------------------------------
// 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
}
}