Fix <rdar://problem/20193929> Can't "return nil" in a convenience initializer

This falls out of the rework I did of 'self' in initializers.  We now correctly
dealloc_ref the allocated object on the failure path of a convenience init.



Swift SVN r27752
This commit is contained in:
Chris Lattner
2015-04-26 05:13:48 +00:00
parent abb34fa1b0
commit 61afbe993f
3 changed files with 18 additions and 29 deletions

View File

@@ -167,9 +167,6 @@ ERROR(assignment_to_immutable_value,sil_analysis,none,
"immutable value '%0' may not be assigned to", "immutable value '%0' may not be assigned to",
(StringRef)) (StringRef))
ERROR(self_init_must_be_called_before_failure,sil_analysis,none,
"failable convenience initializer must delegate to self.init() before "
"returning nil", ())
ERROR(object_not_fully_initialized_before_failure,sil_analysis,none, ERROR(object_not_fully_initialized_before_failure,sil_analysis,none,
"all stored properties of a class instance must be initialized before " "all stored properties of a class instance must be initialized before "
"returning nil from an initializer", ()) "returning nil from an initializer", ())

View File

@@ -1340,8 +1340,8 @@ void LifetimeChecker::processNonTrivialRelease(unsigned ReleaseID) {
// Right now we don't fully support cleaning up a partially initialized object // Right now we don't fully support cleaning up a partially initialized object
// after a failure. Handle this by only allowing an early 'return nil' in an // after a failure. Handle this by only allowing an early 'return nil' in an
// initializer after all properties are initialized. // initializer after all properties are initialized or in a convenience init.
if (TheMemory.isClassInitSelf()) { if (TheMemory.isClassInitSelf() && !TheMemory.isDelegatingInit()) {
SILLocation loc = Release->getLoc(); SILLocation loc = Release->getLoc();
// The release is generally a cleanup in a failure block, and is usually the // The release is generally a cleanup in a failure block, and is usually the
@@ -1358,25 +1358,19 @@ void LifetimeChecker::processNonTrivialRelease(unsigned ReleaseID) {
} }
} }
// If this is a convenience initializer, report that self.init() must be // All members must be initialized (including the base class, if
// called. // present).
if (TheMemory.isDelegatingInit()) { diagnose(Module, loc, diag::object_not_fully_initialized_before_failure);
diagnose(Module, loc, diag::self_init_must_be_called_before_failure);
} else {
// Otherwise all members must be initialized (including the base class, if
// present).
diagnose(Module, loc, diag::object_not_fully_initialized_before_failure);
// Note each of the members that isn't initialized. // Note each of the members that isn't initialized.
DIMemoryUse Use(Release, DIUseKind::Load, 0, TheMemory.NumElements); DIMemoryUse Use(Release, DIUseKind::Load, 0, TheMemory.NumElements);
noteUninitializedMembers(Use); noteUninitializedMembers(Use);
// If we're tracking the state of super.init (i.e., in a derived class) // If we're tracking the state of super.init (i.e., in a derived class)
// then report on failure to call super.init as well. // then report on failure to call super.init as well.
if (TheMemory.isAnyDerivedClassSelf() && if (TheMemory.isAnyDerivedClassSelf() &&
Availability.get(Availability.size()-1) != DIKind::Yes) Availability.get(Availability.size()-1) != DIKind::Yes)
diagnose(Module, loc, diag::must_call_super_init_failable_init); diagnose(Module, loc, diag::must_call_super_init_failable_init);
}
} }
// If it is all 'no' then we can handle is specially without conditional code. // If it is all 'no' then we can handle is specially without conditional code.

View File

@@ -48,7 +48,7 @@ class RootClass {
init() { x = 0; y = 0 } init() { x = 0; y = 0 }
convenience init?(failBeforeDelegation: Bool) { convenience init?(failBeforeDelegation: Bool) {
if failBeforeDelegation { return nil } // expected-error{{failable convenience initializer must delegate to self.init() before returning nil}} if failBeforeDelegation { return nil } // ok
self.init() self.init()
} }
@@ -73,7 +73,7 @@ class RootClass {
} }
convenience init?(failBeforeFailableDelegation: Bool) { convenience init?(failBeforeFailableDelegation: Bool) {
if failBeforeFailableDelegation { return nil } // expected-error {{failable convenience initializer must delegate to self.init() before returning nil}} if failBeforeFailableDelegation { return nil } // ok
self.init(failBeforeInitialization: ()) self.init(failBeforeInitialization: ())
} }
@@ -126,8 +126,7 @@ class SubClass: RootClass {
} }
convenience init?(failBeforeDelegation: Bool) { convenience init?(failBeforeDelegation: Bool) {
if failBeforeDelegation { return nil } if failBeforeDelegation { return nil } // ok
// expected-error@-1{{failable convenience initializer must delegate to self.init() before returning nil}}
self.init() self.init()
} }
@@ -138,7 +137,6 @@ class SubClass: RootClass {
convenience init?(failBeforeFailableDelegation: Bool) { convenience init?(failBeforeFailableDelegation: Bool) {
if failBeforeFailableDelegation { return nil } if failBeforeFailableDelegation { return nil }
// expected-error@-1{{failable convenience initializer must delegate to self.init() before returning nil}}
self.init(failBeforeInitialization: ()) self.init(failBeforeInitialization: ())
} }