mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
We can only do this for two reasons: 1. There is a code path that should have gone through the non-exclusively borrowed self entrypoints, but they were not implemented. 2. We are trying to access self for an argument. By copying the value, we preserve invariants around ownership and also make it easy for DI to catch 2 and not blow up in the case of 1. It is better to error in DI incorrectly, than to hit an unreachable (especially since in non-assert builds, we don't trap at unreachables and just continue to the next function in the text segment). SR-5682 rdar://35402738
1312 lines
34 KiB
Swift
1312 lines
34 KiB
Swift
// RUN: %target-swift-frontend -emit-sil -enable-sil-ownership -primary-file %s -o /dev/null -verify
|
|
|
|
import Swift
|
|
|
|
func markUsed<T>(_ t: T) {}
|
|
|
|
// These are tests for definite initialization, which is implemented by the
|
|
// memory promotion pass.
|
|
|
|
func test1() -> Int {
|
|
// expected-warning @+1 {{variable 'a' was never mutated; consider changing to 'let' constant}} {{3-6=let}}
|
|
var a : Int // expected-note {{variable defined here}}
|
|
return a // expected-error {{variable 'a' used before being initialized}}
|
|
}
|
|
|
|
func takes_inout(_ a: inout Int) {}
|
|
func takes_inout_any(_ a: inout Any) {}
|
|
func takes_closure(_ fn: () -> ()) {}
|
|
|
|
class SomeClass {
|
|
var x : Int // expected-note {{'self.x' not initialized}}
|
|
|
|
var computedProperty : Int { return 42 }
|
|
|
|
init() { x = 0 }
|
|
init(b : Bool) {
|
|
if (b) {}
|
|
} // expected-error {{return from initializer without initializing all stored properties}}
|
|
|
|
func baseMethod() {}
|
|
}
|
|
|
|
struct SomeStruct { var x = 1 }
|
|
|
|
func takesPointer<T>(_: UnsafePointer<T>) {}
|
|
|
|
func test2() {
|
|
// inout.
|
|
|
|
var a1 : Int // expected-note {{variable defined here}}
|
|
takes_inout(&a1) // expected-error {{variable 'a1' passed by reference before being initialized}}
|
|
|
|
var a2 = 4
|
|
takes_inout(&a2) // ok.
|
|
|
|
var a3 : Int
|
|
a3 = 4
|
|
takes_inout(&a3) // ok.
|
|
|
|
var a4 : Int // expected-note {{variable defined here}}
|
|
takesPointer(&a4) // expected-error {{address of variable 'a4' taken before it is initialized}}
|
|
|
|
|
|
// Closures.
|
|
|
|
// expected-warning @+1 {{variable 'b1' was never mutated}} {{3-6=let}}
|
|
var b1 : Int // expected-note {{variable defined here}}
|
|
takes_closure { // expected-error {{variable 'b1' captured by a closure before being initialized}}
|
|
markUsed(b1)
|
|
}
|
|
|
|
var b1a : Int // expected-note {{variable defined here}}
|
|
takes_closure { // expected-error {{variable 'b1a' captured by a closure before being initialized}}
|
|
b1a += 1
|
|
markUsed(b1a)
|
|
}
|
|
|
|
var b2 = 4
|
|
takes_closure { // ok.
|
|
markUsed(b2)
|
|
}
|
|
b2 = 1
|
|
|
|
var b3 : Int
|
|
b3 = 4
|
|
takes_closure { // ok.
|
|
markUsed(b3)
|
|
}
|
|
|
|
var b4 : Int?
|
|
takes_closure { // ok.
|
|
markUsed(b4!)
|
|
}
|
|
b4 = 7
|
|
|
|
let b5: Any
|
|
b5 = "x"
|
|
{ takes_inout_any(&b5) }() // expected-error {{immutable value 'b5' must not be passed inout}}
|
|
({ takes_inout_any(&b5) })() // expected-error {{immutable value 'b5' must not be passed inout}}
|
|
|
|
// Structs
|
|
var s1 : SomeStruct
|
|
s1 = SomeStruct() // ok
|
|
_ = s1
|
|
|
|
var s2 : SomeStruct // expected-note {{variable defined here}}
|
|
s2.x = 1 // expected-error {{struct 's2' must be completely initialized before a member is stored to}}
|
|
|
|
|
|
// Classes
|
|
// expected-warning @+1 {{variable 'c1' was never mutated}} {{3-6=let}}
|
|
var c1 : SomeClass // expected-note {{variable defined here}}
|
|
markUsed(c1.x) // expected-error {{variable 'c1' used before being initialized}}
|
|
|
|
|
|
let c2 = SomeClass()
|
|
markUsed(c2.x) // ok
|
|
|
|
|
|
// Weak
|
|
weak var w1 : SomeClass?
|
|
_ = w1 // ok: default-initialized
|
|
|
|
weak var w2 = SomeClass()
|
|
_ = w2 // ok
|
|
|
|
|
|
// Unowned. This is immediately crashing code (it causes a retain of a
|
|
// released object) so it should be diagnosed with a warning someday.
|
|
// expected-warning @+1 {{variable 'u1' was never mutated; consider changing to 'let' constant}} {{11-14=let}}
|
|
unowned var u1 : SomeClass // expected-note {{variable defined here}}
|
|
_ = u1 // expected-error {{variable 'u1' used before being initialized}}
|
|
|
|
unowned let u2 = SomeClass()
|
|
_ = u2 // ok
|
|
}
|
|
|
|
|
|
|
|
// Tuple field sensitivity.
|
|
func test4() {
|
|
// expected-warning @+1 {{variable 't1' was never mutated; consider changing to 'let' constant}} {{3-6=let}}
|
|
var t1 = (1, 2, 3)
|
|
markUsed(t1.0 + t1.1 + t1.2) // ok
|
|
|
|
|
|
// expected-warning @+1 {{variable 't2' was never mutated; consider changing to 'let' constant}} {{3-6=let}}
|
|
var t2 : (Int, Int, Int) // expected-note 3 {{variable defined here}}
|
|
markUsed(t2.0) // expected-error {{variable 't2.0' used before being initialized}}
|
|
markUsed(t2.1) // expected-error {{variable 't2.1' used before being initialized}}
|
|
markUsed(t2.2) // expected-error {{variable 't2.2' used before being initialized}}
|
|
|
|
|
|
var t3 : (Int, Int, Int) // expected-note {{variable defined here}}
|
|
t3.0 = 1; t3.2 = 42
|
|
markUsed(t3.0)
|
|
markUsed(t3.1) // expected-error {{variable 't3.1' used before being initialized}}
|
|
markUsed(t3.2)
|
|
|
|
|
|
// Partially set, wholly read.
|
|
var t4 : (Int, Int, Int) // expected-note 1 {{variable defined here}}
|
|
t4.0 = 1; t4.2 = 42
|
|
_ = t4 // expected-error {{variable 't4.1' used before being initialized}}
|
|
|
|
|
|
// Subelement sets.
|
|
var t5 : (a : (Int, Int), b : Int) // expected-note {{variable defined here}}
|
|
t5.a = (1,2)
|
|
markUsed(t5.a.0)
|
|
markUsed(t5.a.1)
|
|
markUsed(t5.b) // expected-error {{variable 't5.b' used before being initialized}}
|
|
|
|
|
|
var t6 : (a : (Int, Int), b : Int) // expected-note {{variable defined here}}
|
|
t6.b = 12; t6.a.1 = 1
|
|
markUsed(t6.a.0) // expected-error {{variable 't6.a.0' used before being initialized}}
|
|
markUsed(t6.a.1)
|
|
markUsed(t6.b)
|
|
}
|
|
|
|
func tupleinout(_ a: inout (lo: Int, hi: Int)) {
|
|
markUsed(a.0) // ok
|
|
markUsed(a.1) // ok
|
|
}
|
|
|
|
// Address only types
|
|
func test5<T>(_ x: T, y: T) {
|
|
var a : ((T, T), T) // expected-note {{variable defined here}}
|
|
a.0 = (x, y)
|
|
|
|
_ = a // expected-error {{variable 'a.1' used before being initialized}}
|
|
}
|
|
|
|
|
|
struct IntFloatStruct { var a = 1, b = 2.0 }
|
|
|
|
func test6() -> Int {
|
|
var a = IntFloatStruct()
|
|
|
|
a.a = 1
|
|
a.b = 2
|
|
|
|
return a.a
|
|
}
|
|
|
|
// Control flow.
|
|
func test7(_ cond: Bool) {
|
|
var a : Int
|
|
|
|
if cond { a = 42 } else { a = 17 }
|
|
markUsed(a) // ok
|
|
|
|
var b : Int // expected-note {{variable defined here}}
|
|
if cond { } else { b = 17 }
|
|
markUsed(b) // expected-error {{variable 'b' used before being initialized}}
|
|
}
|
|
|
|
protocol SomeProtocol {
|
|
func protoMe()
|
|
}
|
|
|
|
protocol DerivedProtocol : SomeProtocol {}
|
|
|
|
func existentials(_ i: Int, dp: DerivedProtocol) {
|
|
// expected-warning @+1 {{variable 'a' was written to, but never read}}
|
|
var a : Any = ()
|
|
a = i
|
|
|
|
// expected-warning @+1 {{variable 'b' was written to, but never read}}
|
|
var b : Any
|
|
b = ()
|
|
|
|
// expected-warning @+1 {{variable 'c' was never used}} {{7-8=_}}
|
|
var c : Any // no uses.
|
|
|
|
// expected-warning @+1 {{variable 'd1' was never mutated}} {{3-6=let}}
|
|
var d1 : Any // expected-note {{variable defined here}}
|
|
_ = d1 // expected-error {{variable 'd1' used before being initialized}}
|
|
|
|
|
|
// expected-warning @+1 {{variable 'e' was never mutated}} {{3-6=let}}
|
|
var e : SomeProtocol // expected-note {{variable defined here}}
|
|
e.protoMe() // expected-error {{variable 'e' used before being initialized}}
|
|
|
|
var f : SomeProtocol = dp // ok, init'd by existential upcast.
|
|
|
|
// expected-warning @+1 {{variable 'g' was never mutated}} {{3-6=let}}
|
|
var g : DerivedProtocol // expected-note {{variable defined here}}
|
|
f = g // expected-error {{variable 'g' used before being initialized}}
|
|
_ = f
|
|
}
|
|
|
|
|
|
// Tests for top level code.
|
|
var g1 : Int // expected-note {{variable defined here}}
|
|
var g2 : Int = 42
|
|
|
|
func testTopLevelCode() { // expected-error {{variable 'g1' used by function definition before being initialized}}
|
|
markUsed(g1)
|
|
markUsed(g2)
|
|
}
|
|
|
|
var (g3,g4) : (Int,Int) // expected-note 2 {{variable defined here}}
|
|
class DITLC_Class {
|
|
init() { // expected-error {{variable 'g3' used by function definition before being initialized}}
|
|
markUsed(g3)
|
|
}
|
|
deinit { // expected-error {{variable 'g4' used by function definition before being initialized}}
|
|
markUsed(g4)
|
|
}
|
|
}
|
|
|
|
struct EmptyStruct {}
|
|
|
|
func useEmptyStruct(_ a: EmptyStruct) {}
|
|
|
|
func emptyStructTest() {
|
|
// <rdar://problem/20065892> Diagnostic for an uninitialized constant calls it a variable
|
|
let a : EmptyStruct // expected-note {{constant defined here}}
|
|
useEmptyStruct(a) // expected-error {{constant 'a' used before being initialized}}
|
|
|
|
var (b,c,d) : (EmptyStruct,EmptyStruct,EmptyStruct) // expected-note 2 {{variable defined here}}
|
|
|
|
c = EmptyStruct()
|
|
|
|
useEmptyStruct(b) // expected-error {{variable 'b' used before being initialized}}
|
|
useEmptyStruct(c)
|
|
useEmptyStruct(d) // expected-error {{variable 'd' used before being initialized}}
|
|
|
|
var (e,f) : (EmptyStruct,EmptyStruct)
|
|
(e, f) = (EmptyStruct(),EmptyStruct())
|
|
|
|
useEmptyStruct(e)
|
|
useEmptyStruct(f)
|
|
|
|
var g : (EmptyStruct,EmptyStruct)
|
|
g = (EmptyStruct(),EmptyStruct())
|
|
|
|
useEmptyStruct(g.0)
|
|
useEmptyStruct(g.1)
|
|
}
|
|
|
|
func takesTuplePair(_ a : inout (SomeClass, SomeClass)) {}
|
|
|
|
// This tests cases where a store might be an init or assign based on control
|
|
// flow path reaching it.
|
|
func conditionalInitOrAssign(_ c : Bool, x : Int) {
|
|
var t : Int // Test trivial types.
|
|
if c {
|
|
t = 0
|
|
}
|
|
if c {
|
|
t = x
|
|
}
|
|
t = 2
|
|
_ = t
|
|
|
|
// Nontrivial type
|
|
var sc : SomeClass
|
|
if c {
|
|
sc = SomeClass()
|
|
}
|
|
sc = SomeClass()
|
|
_ = sc
|
|
|
|
// Tuple element types
|
|
var tt : (SomeClass, SomeClass)
|
|
|
|
if c {
|
|
tt.0 = SomeClass()
|
|
} else {
|
|
tt.1 = SomeClass()
|
|
}
|
|
|
|
tt.0 = SomeClass()
|
|
tt.1 = tt.0
|
|
|
|
var t2 : (SomeClass, SomeClass) // expected-note {{variable defined here}}
|
|
t2.0 = SomeClass()
|
|
takesTuplePair(&t2) // expected-error {{variable 't2.1' passed by reference before being initialized}}
|
|
}
|
|
|
|
enum NotInitializedUnion {
|
|
init() {
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
case X
|
|
case Y
|
|
}
|
|
|
|
extension NotInitializedUnion {
|
|
init(a : Int) {
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
}
|
|
|
|
enum NotInitializedGenericUnion<T> {
|
|
init() {
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
case X
|
|
}
|
|
|
|
|
|
class SomeDerivedClass : SomeClass {
|
|
var y : Int
|
|
override init() {
|
|
y = 42 // ok
|
|
super.init()
|
|
}
|
|
|
|
init(a : Bool) {
|
|
super.init() // expected-error {{property 'self.y' not initialized at super.init call}}
|
|
}
|
|
|
|
init(a : Bool, b : Bool) {
|
|
// x is a superclass member. It cannot be used before we are initialized.
|
|
x = 17 // expected-error {{'self' used in property access 'x' before 'super.init' call}}
|
|
y = 42
|
|
super.init()
|
|
}
|
|
|
|
init(a : Bool, b : Bool, c : Bool) {
|
|
y = 42
|
|
super.init()
|
|
}
|
|
|
|
init(a : Bool, b : Bool, c : Bool, d : Bool) {
|
|
y = 42
|
|
super.init()
|
|
super.init() // expected-error {{'super.init' called multiple times in initializer}}
|
|
}
|
|
|
|
init(a : Bool, b : Bool, c : Bool, d : Bool, e : Bool) {
|
|
super.init() // expected-error {{property 'self.y' not initialized at super.init call}}
|
|
super.init() // expected-error {{'super.init' called multiple times in initializer}}
|
|
}
|
|
|
|
init(a : Bool, b : Bool, c : Bool, d : Bool, e : Bool, f : Bool) {
|
|
y = 11
|
|
if a { super.init() }
|
|
x = 42 // expected-error {{'self' used in property access 'x' before 'super.init' call}}
|
|
} // expected-error {{'super.init' isn't called on all paths before returning from initializer}}
|
|
|
|
func someMethod() {}
|
|
|
|
init(a : Int) {
|
|
y = 42
|
|
super.init()
|
|
}
|
|
|
|
init(a : Int, b : Bool) {
|
|
y = 42
|
|
someMethod() // expected-error {{'self' used in method call 'someMethod' before 'super.init' call}}
|
|
super.init()
|
|
}
|
|
|
|
init(a : Int, b : Int) {
|
|
y = 42
|
|
baseMethod() // expected-error {{'self' used in method call 'baseMethod' before 'super.init' call}}
|
|
super.init()
|
|
}
|
|
|
|
init(a : Int, b : Int, c : Int) {
|
|
y = computedProperty // expected-error {{'self' used in property access 'computedProperty' before 'super.init' call}}
|
|
super.init()
|
|
}
|
|
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Delegating initializers
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
class DelegatingCtorClass {
|
|
var ivar: EmptyStruct
|
|
|
|
init() { ivar = EmptyStruct() }
|
|
|
|
convenience init(x: EmptyStruct) {
|
|
self.init()
|
|
_ = ivar // okay: ivar has been initialized by the delegation above
|
|
}
|
|
|
|
convenience init(x: EmptyStruct, y: EmptyStruct) {
|
|
_ = ivar // expected-error {{'self' used in property access 'ivar' before 'self.init' call}}
|
|
ivar = x // expected-error {{'self' used in property access 'ivar' before 'self.init' call}}
|
|
self.init()
|
|
}
|
|
|
|
convenience init(x: EmptyStruct, y: EmptyStruct, z: EmptyStruct) {
|
|
self.init()
|
|
self.init() // expected-error {{'self.init' called multiple times in initializer}}
|
|
}
|
|
|
|
convenience init(x: (EmptyStruct, EmptyStruct)) {
|
|
method() // expected-error {{'self' used in method call 'method' before 'self.init' call}}
|
|
self.init()
|
|
}
|
|
|
|
convenience init(c : Bool) {
|
|
if c {
|
|
return
|
|
}
|
|
self.init()
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
|
|
convenience init(bool: Bool) {
|
|
doesNotReturn()
|
|
}
|
|
|
|
convenience init(double: Double) {
|
|
} // expected-error{{'self.init' isn't called on all paths before returning from initializer}}
|
|
|
|
func method() {}
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Delegating initializers vs extensions
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
protocol TriviallyConstructible {
|
|
init()
|
|
func go(_ x: Int)
|
|
}
|
|
|
|
extension TriviallyConstructible {
|
|
init(down: Int) {
|
|
go(down) // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
self.init()
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Various bugs
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// rdar://16119509 - Dataflow problem where we reject valid code.
|
|
class rdar16119509_Buffer {
|
|
init(x : Int) { }
|
|
}
|
|
class rdar16119509_Base {}
|
|
class rdar16119509_Derived : rdar16119509_Base {
|
|
override init() {
|
|
var capacity = 2
|
|
while capacity < 2 {
|
|
capacity <<= 1
|
|
}
|
|
buffer = rdar16119509_Buffer(x: capacity)
|
|
}
|
|
|
|
var buffer : rdar16119509_Buffer
|
|
}
|
|
|
|
// <rdar://problem/16797372> Bogus error: self.init called multiple times in initializer
|
|
extension Foo {
|
|
convenience init() {
|
|
for _ in 0..<42 {
|
|
}
|
|
self.init(a: 4)
|
|
}
|
|
}
|
|
|
|
class Foo {
|
|
init(a : Int) {}
|
|
}
|
|
|
|
|
|
|
|
func doesNotReturn() -> Never {
|
|
while true {}
|
|
}
|
|
|
|
func doesReturn() {}
|
|
|
|
func testNoReturn1(_ b : Bool) -> Any {
|
|
var a : Any
|
|
if b {
|
|
a = 42
|
|
} else {
|
|
doesNotReturn()
|
|
}
|
|
|
|
return a // Ok, because the noreturn call path isn't viable.
|
|
}
|
|
|
|
func testNoReturn2(_ b : Bool) -> Any {
|
|
var a : Any // expected-note {{variable defined here}}
|
|
if b {
|
|
a = 42
|
|
} else {
|
|
doesReturn()
|
|
}
|
|
|
|
// Not ok, since doesReturn() doesn't kill control flow.
|
|
return a // expected-error {{variable 'a' used before being initialized}}
|
|
}
|
|
|
|
class PerpetualMotion {
|
|
func start() -> Never {
|
|
repeat {} while true
|
|
}
|
|
static func stop() -> Never {
|
|
repeat {} while true
|
|
}
|
|
}
|
|
|
|
func testNoReturn3(_ b : Bool) -> Any {
|
|
let a : Int
|
|
|
|
switch b {
|
|
default:
|
|
PerpetualMotion().start()
|
|
}
|
|
|
|
return a
|
|
}
|
|
|
|
func testNoReturn4(_ b : Bool) -> Any {
|
|
let a : Int
|
|
|
|
switch b {
|
|
default:
|
|
PerpetualMotion.stop()
|
|
}
|
|
|
|
return a
|
|
}
|
|
|
|
|
|
// <rdar://problem/16687555> [DI] New convenience initializers cannot call inherited convenience initializers
|
|
class BaseWithConvenienceInits {
|
|
init(int: Int) {}
|
|
convenience init() {
|
|
self.init(int: 3)
|
|
}
|
|
}
|
|
|
|
class DerivedUsingConvenienceInits : BaseWithConvenienceInits {
|
|
convenience init(string: String) {
|
|
self.init()
|
|
}
|
|
}
|
|
|
|
// <rdar://problem/16660680> QoI: _preconditionFailure() in init method complains about super.init being called multiple times
|
|
class ClassWhoseInitDoesntReturn : BaseWithConvenienceInits {
|
|
init() {
|
|
_preconditionFailure("leave me alone dude");
|
|
}
|
|
}
|
|
|
|
// <rdar://problem/17233681> DI: Incorrectly diagnostic in delegating init with generic enum
|
|
enum r17233681Lazy<T> {
|
|
case Thunk(() -> T)
|
|
case Value(T)
|
|
|
|
init(value: T) {
|
|
self = .Value(value)
|
|
}
|
|
}
|
|
|
|
extension r17233681Lazy {
|
|
init(otherValue: T) {
|
|
self.init(value: otherValue)
|
|
}
|
|
}
|
|
|
|
|
|
// <rdar://problem/17556858> delegating init that delegates to @_transparent init fails
|
|
struct FortyTwo { }
|
|
|
|
extension Double {
|
|
init(v : FortyTwo) {
|
|
self.init(0.0)
|
|
}
|
|
}
|
|
|
|
// <rdar://problem/17686667> If super.init is implicitly inserted, DI diagnostics have no location info
|
|
class r17686667Base {}
|
|
class r17686667Test : r17686667Base {
|
|
var x: Int
|
|
override init() { // expected-error {{property 'self.x' not initialized at implicitly generated super.init call}}
|
|
|
|
}
|
|
}
|
|
|
|
// <rdar://problem/18199087> DI doesn't catch use of super properties lexically inside super.init call
|
|
class r18199087BaseClass {
|
|
let data: Int
|
|
init(val: Int) {
|
|
data = val
|
|
}
|
|
}
|
|
class r18199087SubClassA: r18199087BaseClass {
|
|
init() {
|
|
super.init(val: self.data) // expected-error {{'self' used in property access 'data' before 'super.init' call}}
|
|
}
|
|
}
|
|
|
|
class r18199087BaseClassNonTrivial {
|
|
let data: SomeClass
|
|
init(val: SomeClass) {
|
|
data = val
|
|
}
|
|
}
|
|
|
|
class r18199087SubClassANonTrivial: r18199087BaseClassNonTrivial {
|
|
init() {
|
|
super.init(val: self.data) // expected-error {{'self' used in property access 'data' before 'super.init' call}}
|
|
}
|
|
}
|
|
|
|
// <rdar://problem/18414728> QoI: DI should talk about "implicit use of self" instead of individual properties in some cases
|
|
class rdar18414728Base {
|
|
var prop:String? { return "boo" }
|
|
|
|
// expected-note @+1 {{change 'let' to 'var' to make it mutable}} {{3-6=var}}
|
|
let aaaaa:String // expected-note 3 {{'self.aaaaa' not initialized}}
|
|
|
|
init() {
|
|
if let p1 = prop { // expected-error {{'self' used in property access 'prop' before all stored properties are initialized}}
|
|
aaaaa = p1
|
|
}
|
|
aaaaa = "foo" // expected-error {{immutable value 'self.aaaaa' may only be initialized once}}
|
|
}
|
|
|
|
init(a : ()) {
|
|
method1(42) // expected-error {{'self' used in method call 'method1' before all stored properties are initialized}}
|
|
aaaaa = "foo"
|
|
}
|
|
|
|
init(b : ()) {
|
|
final_method() // expected-error {{'self' used in method call 'final_method' before all stored properties are initialized}}
|
|
aaaaa = "foo"
|
|
}
|
|
|
|
init(c : ()) {
|
|
aaaaa = "foo"
|
|
final_method() // ok
|
|
}
|
|
|
|
func method1(_ a : Int) {}
|
|
final func final_method() {}
|
|
}
|
|
|
|
class rdar18414728Derived : rdar18414728Base {
|
|
var prop2:String? { return "boo" }
|
|
|
|
// expected-note @+1 2 {{change 'let' to 'var' to make it mutable}} {{3-6=var}} {{3-6=var}}
|
|
let aaaaa2:String
|
|
|
|
override init() {
|
|
if let p1 = prop2 { // expected-error {{'self' used in property access 'prop2' before 'super.init' call}}
|
|
aaaaa2 = p1
|
|
}
|
|
aaaaa2 = "foo" // expected-error {{immutable value 'self.aaaaa2' may only be initialized once}}
|
|
super.init()
|
|
}
|
|
|
|
override init(a : ()) {
|
|
method2() // expected-error {{'self' used in method call 'method2' before 'super.init' call}}
|
|
aaaaa2 = "foo"
|
|
super.init()
|
|
}
|
|
|
|
override init(b : ()) {
|
|
aaaaa2 = "foo"
|
|
method2() // expected-error {{'self' used in method call 'method2' before 'super.init' call}}
|
|
super.init()
|
|
}
|
|
|
|
override init(c : ()) {
|
|
super.init() // expected-error {{property 'self.aaaaa2' not initialized at super.init call}}
|
|
aaaaa2 = "foo" // expected-error {{immutable value 'self.aaaaa2' may only be initialized once}}
|
|
method2()
|
|
}
|
|
|
|
func method2() {}
|
|
}
|
|
|
|
struct rdar18414728Struct {
|
|
var computed:Int? { return 4 }
|
|
|
|
var i : Int // expected-note 2 {{'self.i' not initialized}}
|
|
var j : Int // expected-note {{'self.j' not initialized}}
|
|
|
|
init() {
|
|
j = 42
|
|
if let p1 = computed { // expected-error {{'self' used before all stored properties are initialized}}
|
|
i = p1
|
|
}
|
|
i = 1
|
|
}
|
|
init(a : ()) {
|
|
method(42) // expected-error {{'self' used before all stored properties are initialized}}
|
|
i = 1
|
|
j = 2
|
|
}
|
|
|
|
func method(_ a : Int) {}
|
|
}
|
|
|
|
|
|
extension Int {
|
|
mutating func mutate() {}
|
|
func inspect() {}
|
|
}
|
|
|
|
|
|
// <rdar://problem/19035287> let properties should only be initializable, not reassignable
|
|
struct LetProperties {
|
|
// expected-note @+1 {{change 'let' to 'var' to make it mutable}} {{3-6=var}}
|
|
let arr : [Int]
|
|
// expected-note @+1 2 {{change 'let' to 'var' to make it mutable}} {{3-6=var}} {{3-6=var}}
|
|
let (u, v) : (Int, Int)
|
|
// expected-note @+1 2 {{change 'let' to 'var' to make it mutable}} {{3-6=var}} {{3-6=var}}
|
|
let w : (Int, Int)
|
|
let x = 42
|
|
// expected-note @+1 {{change 'let' to 'var' to make it mutable}} {{3-6=var}}
|
|
let y : Int
|
|
let z : Int? // expected-note{{'self.z' not initialized}}
|
|
|
|
// Let properties can be initialized naturally exactly once along any given
|
|
// path through an initializer.
|
|
init(cond : Bool) {
|
|
if cond {
|
|
w.0 = 4
|
|
(u,v) = (4,2)
|
|
y = 71
|
|
} else {
|
|
y = 13
|
|
v = 2
|
|
u = v+1
|
|
w.0 = 7
|
|
}
|
|
w.1 = 19
|
|
z = nil
|
|
arr = []
|
|
}
|
|
|
|
// Multiple initializations are an error.
|
|
init(a : Int) {
|
|
y = a
|
|
y = a // expected-error {{immutable value 'self.y' may only be initialized once}}
|
|
|
|
u = a
|
|
v = a
|
|
u = a // expected-error {{immutable value 'self.u' may only be initialized once}}
|
|
v = a // expected-error {{immutable value 'self.v' may only be initialized once}}
|
|
|
|
w.0 = a
|
|
w.1 = a
|
|
|
|
w.0 = a // expected-error {{immutable value 'self.w.0' may only be initialized once}}
|
|
w.1 = a // expected-error {{immutable value 'self.w.1' may only be initialized once}}
|
|
|
|
arr = []
|
|
arr = [] // expected-error {{immutable value 'self.arr' may only be initialized once}}
|
|
} // expected-error {{return from initializer without initializing all stored properties}}
|
|
|
|
// inout uses of let properties are an error.
|
|
init() {
|
|
u = 1; v = 13; w = (1,2); y = 1 ; z = u
|
|
|
|
var variable = 42
|
|
swap(&u, &variable) // expected-error {{immutable value 'self.u' must not be passed inout}}
|
|
|
|
u.inspect() // ok, non mutating.
|
|
u.mutate() // expected-error {{mutating method 'mutate' may not be used on immutable value 'self.u'}}
|
|
|
|
arr = []
|
|
arr += [] // expected-error {{mutating operator '+=' may not be used on immutable value 'self.arr'}}
|
|
arr.append(4) // expected-error {{mutating method 'append' may not be used on immutable value 'self.arr'}}
|
|
arr[12] = 17 // expected-error {{mutating subscript 'subscript' may not be used on immutable value 'self.arr'}}
|
|
}
|
|
}
|
|
|
|
|
|
// <rdar://problem/19215313> let properties don't work with protocol method dispatch
|
|
protocol TestMutabilityProtocol {
|
|
func toIntMax()
|
|
mutating func changeToIntMax()
|
|
}
|
|
|
|
class C<T : TestMutabilityProtocol> {
|
|
let x : T
|
|
let y : T
|
|
|
|
init(a : T) {
|
|
x = a; y = a
|
|
x.toIntMax()
|
|
y.changeToIntMax() // expected-error {{mutating method 'changeToIntMax' may not be used on immutable value 'self.y'}}
|
|
}
|
|
}
|
|
|
|
struct MyMutabilityImplementation : TestMutabilityProtocol {
|
|
func toIntMax() {
|
|
}
|
|
|
|
mutating func changeToIntMax() {
|
|
}
|
|
}
|
|
|
|
|
|
// <rdar://problem/16181314> don't require immediate initialization of 'let' values
|
|
func testLocalProperties(_ b : Int) -> Int {
|
|
// expected-note @+1 {{change 'let' to 'var' to make it mutable}} {{3-6=var}}
|
|
let x : Int
|
|
let y : Int // never assigned is ok expected-warning {{immutable value 'y' was never used}} {{7-8=_}}
|
|
|
|
x = b
|
|
x = b // expected-error {{immutable value 'x' may only be initialized once}}
|
|
|
|
// This is ok, since it is assigned multiple times on different paths.
|
|
let z : Int
|
|
if true || false {
|
|
z = b
|
|
} else {
|
|
z = b
|
|
}
|
|
|
|
_ = z
|
|
return x
|
|
}
|
|
|
|
// Should be rejected as multiple assignment.
|
|
func testAddressOnlyProperty<T>(_ b : T) -> T {
|
|
// expected-note @+1 {{change 'let' to 'var' to make it mutable}} {{3-6=var}}
|
|
let x : T
|
|
let y : T
|
|
let z : T // never assigned is ok. expected-warning {{immutable value 'z' was never used}} {{7-8=_}}
|
|
x = b
|
|
y = b
|
|
x = b // expected-error {{immutable value 'x' may only be initialized once}}
|
|
|
|
var tmp = b
|
|
swap(&x, &tmp) // expected-error {{immutable value 'x' must not be passed inout}}
|
|
return y
|
|
}
|
|
|
|
|
|
// <rdar://problem/19254812> DI bug when referencing let member of a class
|
|
class r19254812Base {}
|
|
class r19254812Derived: r19254812Base{
|
|
let pi = 3.14159265359
|
|
|
|
init(x : ()) {
|
|
markUsed(pi) // ok, no diagnostic expected.
|
|
}
|
|
}
|
|
|
|
|
|
// <rdar://problem/19259730> Using mutating methods in a struct initializer with a let property is rejected
|
|
struct StructMutatingMethodTest {
|
|
let a, b: String // expected-note 2 {{'self.b' not initialized}}
|
|
|
|
init(x: String, y: String) {
|
|
a = x
|
|
b = y
|
|
mutate() // ok
|
|
}
|
|
|
|
init(x: String) {
|
|
a = x
|
|
mutate() // expected-error {{'self' used before all stored properties are initialized}}
|
|
b = x
|
|
}
|
|
|
|
init() {
|
|
a = ""
|
|
nonmutate() // expected-error {{'self' used before all stored properties are initialized}}
|
|
b = ""
|
|
}
|
|
|
|
mutating func mutate() {}
|
|
func nonmutate() {}
|
|
}
|
|
|
|
// <rdar://problem/19268443> DI should reject this call to transparent function
|
|
class TransparentFunction {
|
|
let x : Int
|
|
let y : Int
|
|
init() {
|
|
x = 42
|
|
x += 1 // expected-error {{mutating operator '+=' may not be used on immutable value 'self.x'}}
|
|
|
|
y = 12
|
|
myTransparentFunction(&y) // expected-error {{immutable value 'self.y' must not be passed inout}}
|
|
}
|
|
}
|
|
|
|
@_transparent
|
|
func myTransparentFunction(_ x : inout Int) {}
|
|
|
|
|
|
// <rdar://problem/19782264> Immutable, optional class members can't have their subproperties read from during init()
|
|
class MyClassWithAnInt {
|
|
let channelCount : Int = 42
|
|
}
|
|
class MyClassTestExample {
|
|
let clientFormat : MyClassWithAnInt!
|
|
|
|
init(){
|
|
clientFormat = MyClassWithAnInt()
|
|
_ = clientFormat.channelCount
|
|
}
|
|
}
|
|
|
|
|
|
// <rdar://problem/19746552> QoI: variable "used before being initialized" instead of "returned uninitialized" in address-only enum/struct
|
|
|
|
struct AddressOnlyStructWithInit<T, U> {
|
|
let a : T?
|
|
let b : U? // expected-note {{'self.b' not initialized}}
|
|
|
|
init(a : T) {
|
|
self.a = a
|
|
} // expected-error {{return from initializer without initializing all stored properties}}
|
|
}
|
|
|
|
enum AddressOnlyEnumWithInit<T> {
|
|
case X(T), Y
|
|
|
|
init() {
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
}
|
|
|
|
|
|
// <rdar://problem/20135113> QoI: enum failable init that doesn't assign to self produces poor error
|
|
enum MyAwesomeEnum {
|
|
case One, Two
|
|
|
|
init?() {
|
|
|
|
} // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
}
|
|
|
|
// <rdar://problem/20679379> DI crashes on initializers on protocol extensions
|
|
extension SomeProtocol {
|
|
init?() {
|
|
let a = self // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
self = a
|
|
}
|
|
|
|
init(a : Int) {
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
|
|
init(c : Float) {
|
|
protoMe() // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
}
|
|
|
|
|
|
|
|
// Lvalue check when the archetypes are not the same.
|
|
struct LValueCheck<T> {
|
|
// expected-note @+1 {{change 'let' to 'var' to make it mutable}} {{3-6=var}}
|
|
let x = 0 // expected-note {{initial value already provided in 'let' declaration}}
|
|
}
|
|
|
|
extension LValueCheck {
|
|
init(newY: Int) {
|
|
x = 42 // expected-error {{immutable value 'self.x' may only be initialized once}}
|
|
}
|
|
}
|
|
|
|
// <rdar://problem/20477982> Accessing let-property with default value in init() can throw spurious error
|
|
struct DontLoadFullStruct {
|
|
let x: Int = 1
|
|
let y: Int
|
|
init() {
|
|
y = x // ok!
|
|
}
|
|
}
|
|
|
|
|
|
func testReassignment() {
|
|
let c : Int // expected-note {{change 'let' to 'var' to make it mutable}} {{3-6=var}}
|
|
c = 12
|
|
c = 32 // expected-error {{immutable value 'c' may only be initialized once}}
|
|
_ = c
|
|
}
|
|
|
|
|
|
// <rdar://problem/21295093> Swift protocol cannot implement default initializer
|
|
protocol ProtocolInitTest {
|
|
init()
|
|
init(a : Int)
|
|
|
|
var i: Int { get set }
|
|
}
|
|
|
|
extension ProtocolInitTest {
|
|
init() {
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
|
|
init(b : Float) {
|
|
self.init(a: 42) // ok
|
|
}
|
|
|
|
// <rdar://problem/21684596> QoI: Poor DI diagnostic in protocol extension initializer
|
|
init(test1 ii: Int) {
|
|
i = ii // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
self.init()
|
|
}
|
|
|
|
init(test2 ii: Int) {
|
|
self = unsafeBitCast(0, to: Self.self)
|
|
i = ii
|
|
}
|
|
|
|
init(test3 ii: Int) {
|
|
i = ii // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
self = unsafeBitCast(0, to: Self.self)
|
|
}
|
|
|
|
init(test4 ii: Int) {
|
|
i = ii // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
|
|
}
|
|
|
|
// <rdar://problem/22436880> Function accepting UnsafeMutablePointer is able to change value of immutable value
|
|
func bug22436880(_ x: UnsafeMutablePointer<Int>) {}
|
|
func test22436880() {
|
|
let x: Int
|
|
x = 1
|
|
bug22436880(&x) // expected-error {{immutable value 'x' must not be passed inout}}
|
|
}
|
|
|
|
// sr-184
|
|
let x: String? // expected-note 2 {{constant defined here}}
|
|
print(x?.count as Any) // expected-error {{constant 'x' used before being initialized}}
|
|
print(x!) // expected-error {{constant 'x' used before being initialized}}
|
|
|
|
|
|
// <rdar://problem/22723281> QoI: [DI] Misleading error from Swift compiler when using an instance method in init()
|
|
protocol PMI {
|
|
func getg()
|
|
}
|
|
|
|
extension PMI {
|
|
func getg() {}
|
|
}
|
|
|
|
class WS: PMI {
|
|
final let x: String // expected-note {{'self.x' not initialized}}
|
|
|
|
init() {
|
|
getg() // expected-error {{'self' used in method call 'getg' before all stored properties are initialized}}
|
|
self.x = "foo"
|
|
}
|
|
}
|
|
|
|
// <rdar://problem/23013334> DI QoI: Diagnostic claims that property is being used when it actually isn't
|
|
class r23013334 {
|
|
var B: Int // expected-note {{'self.B' not initialized}}
|
|
var A: String
|
|
|
|
init(A: String) throws {
|
|
self.A = A
|
|
self.A.withCString { cString -> () in // expected-error {{'self' captured by a closure before all members were initialized}}
|
|
|
|
print(self.A)
|
|
return ()
|
|
}
|
|
|
|
self.B = 0
|
|
}
|
|
|
|
}
|
|
|
|
class r23013334Derived : rdar16119509_Base {
|
|
var B: Int // expected-note {{'self.B' not initialized}}
|
|
var A: String
|
|
|
|
init(A: String) throws {
|
|
self.A = A
|
|
self.A.withCString { cString -> () in // expected-error {{'self' captured by a closure before all members were initialized}}
|
|
|
|
print(self.A)
|
|
return ()
|
|
}
|
|
|
|
self.B = 0
|
|
}
|
|
|
|
}
|
|
|
|
// sr-1469
|
|
struct SR1469_Struct1 {
|
|
let a: Int
|
|
let b: Int // expected-note {{'self.b' not initialized}}
|
|
|
|
init?(x: Int, y: Int) {
|
|
self.a = x
|
|
if y == 42 {
|
|
return // expected-error {{return from initializer without initializing all stored properties}}
|
|
}
|
|
// many lines later
|
|
self.b = y
|
|
}
|
|
}
|
|
|
|
struct SR1469_Struct2 {
|
|
let a: Int
|
|
let b: Int // expected-note {{'self.b' not initialized}}
|
|
|
|
init?(x: Int, y: Int) {
|
|
self.a = x
|
|
return // expected-error {{return from initializer without initializing all stored properties}}
|
|
}
|
|
}
|
|
|
|
struct SR1469_Struct3 {
|
|
let a: Int
|
|
let b: Int // expected-note {{'self.b' not initialized}}
|
|
|
|
init?(x: Int, y: Int) {
|
|
self.a = x
|
|
if y == 42 {
|
|
self.b = y
|
|
return
|
|
}
|
|
} // expected-error {{return from initializer without initializing all stored properties}}
|
|
}
|
|
|
|
enum SR1469_Enum1 {
|
|
case A, B
|
|
|
|
init?(x: Int) {
|
|
if x == 42 {
|
|
return
|
|
}
|
|
// many lines later
|
|
self = .A
|
|
} // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
}
|
|
|
|
enum SR1469_Enum2 {
|
|
case A, B
|
|
|
|
init?() {
|
|
return
|
|
} // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
}
|
|
enum SR1469_Enum3 {
|
|
case A, B
|
|
|
|
init?(x: Int) {
|
|
if x == 42 {
|
|
self = .A
|
|
return
|
|
}
|
|
} // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
|
|
}
|
|
|
|
class BadFooSuper {
|
|
init() {}
|
|
init(_ x: BadFooSuper) {}
|
|
}
|
|
|
|
class BadFooSubclass: BadFooSuper {
|
|
override init() {
|
|
super.init(self) // expected-error {{'self' used before 'super.init' call}}
|
|
}
|
|
}
|
|
|
|
class SuperConvenienceBase {
|
|
public init(_ i: Int) {}
|
|
public convenience init(_ i1: Int, _ i2: Int) {
|
|
self.init(i2)
|
|
}
|
|
}
|
|
|
|
class SuperConvenienceSub : SuperConvenienceBase {
|
|
public override init(_ i: Int) {
|
|
super.init(i)
|
|
}
|
|
public init(_ i1: Int, _ i2: Int, _ i3: Int) {
|
|
self.init(i1, i1)
|
|
}
|
|
}
|
|
|
|
// While testing some changes I found this regression that wasn't
|
|
// covered by any existing tests
|
|
class Base {}
|
|
|
|
func makeAnAny() -> Any { return 3 }
|
|
|
|
class Derived : Base {
|
|
var x: Int?
|
|
var y: Int?
|
|
|
|
override init() {
|
|
x = makeAnAny() as? Int
|
|
y = makeAnAny() as? Int
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
// This test makes sure that we properly error (but don't crash) when calling a
|
|
// subclass method as an argument to a super.init.
|
|
class MethodTestParent {
|
|
init(i: Int) {}
|
|
}
|
|
|
|
class MethodTestChild : MethodTestParent {
|
|
init() {
|
|
super.init(i: getInt()) // expected-error {{'self' used in method call 'getInt' before 'super.init' call}}
|
|
}
|
|
|
|
init(val: ()) {
|
|
// Currently we squelch the inner error of using self in method call for 'getInt2'
|
|
super.init(i: getInt2(x: self)) // expected-error {{'self' used in method call 'getInt2' before 'super.init' call}}
|
|
}
|
|
|
|
func getInt() -> Int {
|
|
return 0
|
|
}
|
|
|
|
func getInt2(x: MethodTestChild) -> Int {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// This test makes sure that if we cast self to a protocol (implicitly or not), we properly error.
|
|
protocol ProtocolCastTestProtocol : class {
|
|
}
|
|
|
|
class ProtocolCastTestParent {
|
|
init(foo f: ProtocolCastTestProtocol) {
|
|
}
|
|
|
|
init(foo2 f: Any) {
|
|
}
|
|
}
|
|
|
|
class ProtocolCastTestChild : ProtocolCastTestParent, ProtocolCastTestProtocol {
|
|
private let value: Int
|
|
|
|
init(value1 v: Int) {
|
|
value = v
|
|
super.init(foo: self) // expected-error {{'self' used before 'super.init' call}}
|
|
}
|
|
|
|
init(value2 v: Int) {
|
|
value = v
|
|
super.init(foo: self as ProtocolCastTestProtocol) // expected-error {{'self' used before 'super.init' call}}
|
|
}
|
|
|
|
init(value3 v: Int) {
|
|
value = v
|
|
super.init(foo2: self) // expected-error {{'self' used before 'super.init' call}}
|
|
}
|
|
|
|
init(value4 v: Int) {
|
|
value = v
|
|
super.init(foo2: self as Any) // expected-error {{'self' used before 'super.init' call}}
|
|
}
|
|
}
|
|
|