-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Property wrappers] Fix handling of @autoclosure in init(wrappedValue:) #30537
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
[Property wrappers] Fix handling of @autoclosure in init(wrappedValue:) #30537
Conversation
If the 'wrappedValue:' parameter is an escaping autoclosure, and a struct property is marked with that property wrapper, the memberwise initializer of the struct is now synthesized with an escaping autoclosure for that property.
When assigning to a wrapped variable in 'init()', if the property wrapper's 'wrappedValue:' parameter is an escaping autoclosure, the initializer and setter have incompatible types -- the setter takes a value, and initializer takes a closure returning that value. An assign_by_wrapper with incompatible types causes SIL verification to fail. This commit makes the SIL not use assign_by_wrapper in such cases, and use the setter directly, resulting in a definitive initialization (DI) error instead. struct S { @lazy var n: Int // Lazy.init(wrappedValue: @autoclosure ... ) init() { n = 1 // error: 'self' used before all stored properties are initialized } }
GetterSetterComponent::hasPropertyWrapper() now checks for more stuff than just whether there's a property wrapper or not. It's therefore renamed to canRewriteSetAsPropertyWrapperInit().
@swift-ci please smoke test |
The Linux failure looks spurious, but I've found another issue in testing that I'd like to address before merging. |
Fixes the test case from rdar://problem/58986940.
@swift-ci please smoke test |
@swift-ci please test source compatibility |
@swift-ci please test source compatibility |
@swift-ci please test Linux |
Build failed |
// created when we have a property wrapper's 'init(wrappedValue:)' | ||
// taking an autoclosure argument. | ||
if (auto ace = dyn_cast<AutoClosureExpr>(e)) { | ||
if (auto ce = dyn_cast<CallExpr>(ace->getSingleExpressionBody())) { |
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 seems really, really brittle. Is there a way we can keep this in sync with the synthesis code?
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.
I haven't come up with a good way, no. In general we really need a better way to pull out the initial value expression from the type-checked AST for the initialization of the backing storage, because that code also needed too much tweaking. Maybe there's some better way to grab the expression at the right point in CSApply.cpp
so we can save it.
Note: this pull request picks up #26572 and fixes
a few merge conflicts. All thanks go to @roop
If the ‘wrappedValue:’ parameter is an escaping autoclosure, and a
struct property is marked with that property wrapper, the memberwise
initializer of the struct is now synthesized with an escaping
autoclosure for that property.
Resolves SR-10950.
Given a Lazy property wrapper:
and a struct using the property wrapper
the memberwise initializer for S is currently synthesized as:
This is incorrect because if we create S like
var s1 = S()
or likevar s2 = S(n: computeInt())
,computeInt()
will get evaluated at the time of creation of S, which@autoclosure
specification@Lazy
not really initialize things lazilyTo fix this, this PR makes the memberwise initializer get generated as:
Implementation details: