Skip to content
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

[SR-11280] PropertyWrapper property + didSet Property Observer results in get before set failure #53681

Closed
daveanderson mannequin opened this issue Aug 9, 2019 · 7 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself property wrappers Feature: property wrappers SILGen Area → compiler: The SIL generation stage

Comments

@daveanderson
Copy link
Mannequin

daveanderson mannequin commented Aug 9, 2019

Previous ID SR-11280
Radar rdar://problem/54133764
Original Reporter @daveanderson
Type Bug
Status Closed
Resolution Done

Attachment: Download

Environment

Xcode Version 11.0 beta 5 (11M382q)

Please try this out in the attached playground.

See also FB6975769

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, PropertyWrappers, SILGen
Assignee @theblixguy
Priority Medium

md5: 7039dab2460e6310b9ed7f65d3540b71

is duplicated by:

  • SR-11297 Swift should not synthesize an access to "oldValue" when it is not needed

Issue Description:

Using a property wrapper equivalent to the LateInitialized example from WWDC 2019 in combination with a `didSet` side effect on the same property causes the wrappedValue getter to be called as part of setting the value.

I believe the combination of PropertyWrapper and didSet Property Observer are incompatible in this case because the initialization of the storage has not occurred when the oldValue getter occurred. The diagnostics around this issue are incomplete.

See attached playground for example. Changing the didSet to a willSet avoids the get-before-set error but is not a desirable workaround.

Succinctly

struct Value {}

class State: ObservableObject {
    @DeferredInitialization public var value: Value {
        // NOTE: the presence of a didSet on this @propertyWrapped value causes the getter to be called when the value is being set
        // Changing this from didSet to willSet avoids the error but may not result in correct behaviour
        didSet {
            print("side effect")
        }
    }
}

let state = State()
state.value = Value()

Attempting to set state.value in this way causes the @DeferredInitialization wrappedValue getter to be before the actual value is set. Presumably this occurs because the didSet is capturing the oldValue from uninitialized storage.

This may be an intractable problem because these two features should not be combined in this manner, however at a minimum a diagnostic should appear or the use of oldValue in the didSet should be the trigger for this issue.

I did not expect didSet to have the side effect of calling a getter whose PropertyWrapper backing storage has not been initialized.

Ideally a LateInitialized/DeferredInitialization property wrapper should be part of the standard library and include an appropriate workaround such that a didSet Property Observer can be used in combination with the property wrapper as long as the oldValue is not accessed.

In some ways the “magic” of oldValue is surprising. It seems exceptional in swift that

didSet {
  print(oldValue) 
}

is possible as the provenance of oldValue is “magic”.

Its almost as though didSet should work more like

didSet { oldValue in

}

where

didSet { _ in

}

And

didSet { 

}

Would not trigger the oldValue getter and thus be more compatible with a propertyWrapper of this style.

Understandably this would be a significant, code-breaking change to the syntax of the didSet property observer.

@beccadax
Copy link
Contributor

Reproduced in beta 5. This also reproduces when the playground's Contents.swift file is run from the command line.

@theblixguy
Copy link
Collaborator

cc @DougGregor

@theblixguy
Copy link
Collaborator

Actually I have a fix for this - this is related to SR-11297 and SR-5982 i.e. calling the getter even when we're not using the oldValue in the didSet.

@theblixguy
Copy link
Collaborator

PR for fix is here: #26632

@daveanderson
Copy link
Mannequin Author

daveanderson mannequin commented Aug 21, 2019

Thanks for the PR @theblixguy as well as the related pitch!

@theblixguy
Copy link
Collaborator

Implemented on master, please verify using the next available trunk snapshot!

@daveanderson
Copy link
Mannequin Author

daveanderson mannequin commented Jan 23, 2022

Thanks Suyash!

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself property wrappers Feature: property wrappers SILGen Area → compiler: The SIL generation stage
Projects
None yet
Development

No branches or pull requests

2 participants