-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Fix for class convenience initializers delegating to factory initializers (3.0) #4604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix for class convenience initializers delegating to factory initializers (3.0) #4604
Conversation
Previously, we were only able to detect factory initializers dispatched through class_method. This didn't work for factory initializers defined in protocol extensions. The end result would be that we would strong_release an uninitialized class instance, which could cause crashes. Fix DI to correctly release the old instance using dealloc_partial_ref instead. Fixes <rdar://problem/27713221>.
I apologize in advance to @jrose-apple, who is not a fan of this fix ;-) In unoptimized builds, the convenience initializers on DispatchQueue allocate and immediately deallocate an instance of OS_dispatch_queue prior to calling the C function that returns the "real" instance. This is because we don't have a way to write user-defined factory initializers yet; convenience initializers still have an 'initializing' entry point that takes an existing instance, which we have no choice but to throw away. Unfortunately, when we perform the fake allocation, we look up class metadata by calling the wrong Swift runtime function, causing a crash when we send +allocWithZone:. Fix this so that the metadata is accessed via a lookup from the Objective-C runtime, instead of making a totally fake 'foreign metadata' object -- it looks like there was code for this already, it just wasn't used in all cases. While getting metadata for a runtime-only class should be rare, this feels like a real bug fix, to me. Second, we would ultimately free the fake object by sending -release, however OS_dispatch_queue has an override of -dealloc which doesn't like to be called with a completely uninitialized instance. Here, I'm going to drop all pretense of sanity. The patch just changes IRGen to lower the dealloc_partial_ref instruction as a call to the object_dispose() Objective-C runtime function when the class in question is a runtime-only class. This frees the object without running -dealloc, which *happens* to work for OS_dispatch_queue. Fixes <rdar://problem/27226313>.
@swift-ci Please test OS X |
@jrose-apple Do you mind reviewing this, since you asked for the cherry-pick :) |
I don't think I'm qualified to review the DI change, sorry. The IRGen change looks just as icky-but-valid as it did before. |
@DougGregor did some work on DI factory initializers recently, maybe he can review it. |
// delegating to a factory initializer. In this case, the object | ||
// was allocated with +allocWithZone:, so calling object_dispose() | ||
// should be OK. | ||
if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::RuntimeOnly) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why shouldn't runtime-only classes be freed with-release
? We know that we have a real Objective-C object that we're freeing, and the only thing that makes them "run-time only" is that the class and metaclass symbols are hidden.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the "Jordan hates this patch" as mentioned in the commit message. It's not really the correct fix for deallocating a partially-allocated foreign class, but in practice it'll do the right thing and release
won't, because some of them are in a state where dealloc
shouldn't be called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That same logic is equally true for non-runtime-only Objective-C classes for which we zero-initialized state doesn't imply that -dealloc
is safe. I can live with this as a hack to improve on the status quo, but... ewww.
The DI parts look reasonable to me. |
[pull] swiftwasm from main
Uh oh!
There was an error while loading. Please reload this page.