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-12483] Weak variables deallocated too quickly in optimised (-O) builds #54923

Closed
swift-ci opened this issue Apr 1, 2020 · 6 comments
Closed
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself modifiers Feature: Declaration and type modifiers not a bug Resolution → not a bug: Reported as a bug but turned out to be expected behavior or programmer error optimized only Flag: An issue whose reproduction requires optimized compilation reference ownership Feature → modifiers: Reference storage ownership modifiers swift 5.2 unexpected behavior Bug: Unexpected behavior or incorrect output weak Feature → modifiers → reference ownership: Weak

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Apr 1, 2020

Previous ID SR-12483
Radar rdar://problem/62202438
Original Reporter Aquis (JIRA User)
Type Bug
Environment

Apple Swift version 5.2 (swiftlang-1103.0.32.1 clang-1103.0.32.29)
Target: x86_64-apple-darwin19.4.0

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: b87ad1b64715ab96b0b6f6b5dfcfb1c2

Issue Description:

I noticed some strange errors in my iOS project when it was built for release in Swift 5.2, and I was eventually able to create a small test case. It seems that weak variables are deallocated too quickly (or at the very least, they have different semantics between optimised and unoptimised builds). The following produces a different output depending on optimisation:

final class Object {}

final class Wrapper {
    weak var contents: Object? // Weak object declared here
    
    init(contents: Object? = nil) {
        self.contents = contents
    }
}

func test() -> Object? {
    let object: Object // Requires separate lines to replicate
    object = Object() // Strong variable should keep it alive
    
    let wrapper = Wrapper(contents: object)
    
    return wrapper.contents
}

print(test() as Any)

Interestingly, as annotated above, the declaration and the assignment have to be on different lines to replicate. The non-optimised build operates how I expect, the optimised build does not.

@beccadax
Copy link
Contributor

@swift-ci create

@eeckstein
Copy link
Member

Aquis (JIRA User) Though it sounds not very intuitive, this is expected behavior. Let me explain:
The end-of-lifetime for an object is not defined by the end of its lexical scope, but by its last use. The optimizer shrinks lifetimes as good as it can.
In 5.2 we "improved" the optimizer and that's the reason why you didn't see this behavior in previous swift versions.

@swift-ci
Copy link
Collaborator Author

Comment by Robert Pugh (JIRA)

@eeckstein

1 - Shouldn't optimised and non-optimised builds produce binaries which behave in the same way? Otherwise people are going to assume their code works when it doesn't.

2 - Is the fact that it requires the definition and assignment on different lines a bug? I would never expect this choice in syntax to alter the logic.

3 - If this is really desired behaviour, is there some non-hacky way to overcome it? E.g. to construct an object in one method and pass it to something which assigns it as a weak property while it does something with it, without it immediately getting released? Can I force that strong reference throughout the lifetime of the original method?

@eeckstein
Copy link
Member

Aquis (JIRA User)
1 - Ideally yes. But there are a few aspects of the language where the programmer cannot make assumptions (e.g. the exact point where objects are destroyed). It's just too important for performance to let the optimizer be flexible.

2 - It's not a bug, it's a missed optimization opportunity. Having the assignment in a different line triggers different internal code generation.

3 - Yes!

func test() -> Object? {
  let object: Object
  object = Object()

  let wrapper = Wrapper(contents: object)
    
  return withExtendedLifetime(object) {
    return wrapper.contents
  }
}

@swift-ci
Copy link
Collaborator Author

Comment by Robert Pugh (JIRA)

Perfect @eeckstein, I wasn't aware of withExtendedLifetime. Thanks!

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@amomchilov
Copy link
Contributor

Looks like this issue can be closed

@AnthonyLatsis AnthonyLatsis added ARC Feature: automatic reference counting memory safety Feature: memory safety unexpected behavior Bug: Unexpected behavior or incorrect output reference ownership Feature → modifiers: Reference storage ownership modifiers modifiers Feature: Declaration and type modifiers weak Feature → modifiers → reference ownership: Weak not a bug Resolution → not a bug: Reported as a bug but turned out to be expected behavior or programmer error swift 5.2 optimized only Flag: An issue whose reproduction requires optimized compilation labels Mar 23, 2024
@AnthonyLatsis AnthonyLatsis closed this as not planned Won't fix, can't repro, duplicate, stale Mar 23, 2024
@AnthonyLatsis AnthonyLatsis removed ARC Feature: automatic reference counting memory safety Feature: memory safety labels Mar 23, 2024
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 modifiers Feature: Declaration and type modifiers not a bug Resolution → not a bug: Reported as a bug but turned out to be expected behavior or programmer error optimized only Flag: An issue whose reproduction requires optimized compilation reference ownership Feature → modifiers: Reference storage ownership modifiers swift 5.2 unexpected behavior Bug: Unexpected behavior or incorrect output weak Feature → modifiers → reference ownership: Weak
Projects
None yet
Development

No branches or pull requests

5 participants