Skip to content

Annotate Foundation overlay with __shared/__owned #19143

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

Merged

Conversation

itaiferber
Copy link
Contributor

@itaiferber itaiferber commented Sep 5, 2018

What's in this pull request?
Annotates portions of the Foundation overlay with the appropriate __shared/__owned annotations following the patterns we use (resolving rdar://problem/34989766). I looked at every declared func and init within our overlay and annotated all methods as appropriate. ~~~A search of all stored vars shows no necessary changes AFAICT as we don't expose reference types from our Swift APIs.~~~ (Since var types cannot be annotated for ownership, there's nothing for us to do in the cases where vars should be __shared.)

Overall the changes mostly revolve around bridging:

  • We often copy() the passed NS type before holding onto it as a reference to preserve value semantics, and in those cases, the parameter should be __shared as we don't hold on to the original. (AFAIK, copy() should help enforce the right refcounting in the case where copy() returns self.)
  • In other cases, we create an NS type out of the Swift type via bridging (which copies as well) and hold on to the NS type

There are a few cases where init parameters go unused (such as in String.init?(contentsOf:)), and some cases around KeyPath where we appear to not store the given KeyPath but it actually ends up stored in a global table.

@itaiferber
Copy link
Contributor Author

@swift-ci Please test macOS

@itaiferber
Copy link
Contributor Author

@swift-ci Please smoke test Linux

@itaiferber
Copy link
Contributor Author

@eeckstein As far as I'm aware, our actual static bridging functions (_forceBridgeFromObjectiveC, _conditionallyBridgeFromObjectiveC, and _unconditionallyBridgeFromObjectiveC) should not require annotations as:

  1. They're static funcs which have no local storage (or write out to inout-params)
  2. Since they call into the shared reference variants changed above, the default for funcs applies here — the params should not be owned

Is my understanding here correct?

@jrose-apple
Copy link
Contributor

Sorry, why is this specific to reference types? cc @airspeedswift

@itaiferber
Copy link
Contributor Author

@jrose-apple Sorry, have I missed something here? Is this not inherently related to values which require reference counting?

@jrose-apple
Copy link
Contributor

"Values which require reference counting" isn't quite the same as "reference types". String and Array and Data all require reference counting too, right?

@itaiferber
Copy link
Contributor Author

Ah, therein lies my misunderstanding about what was necessary here. I'll re-audit for those types as well.

@itaiferber
Copy link
Contributor Author

Closing for now.

@itaiferber itaiferber closed this Sep 5, 2018
@swift-ci
Copy link
Contributor

swift-ci commented Sep 5, 2018

Build failed
Swift Test OS X Platform
Git Sha - 402457b

@itaiferber
Copy link
Contributor Author

Made another pass here to add annotations to cover other non-trivial types. Will be doing another pass to confirm there's nothing I missed.

@itaiferber itaiferber reopened this Sep 11, 2018
@itaiferber
Copy link
Contributor Author

@swift-ci Please test macOS

@itaiferber
Copy link
Contributor Author

@swift-ci Please smoke test Linux

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 402457b

@itaiferber
Copy link
Contributor Author

@jrose-apple @eeckstein Do the changes here look correct? Does anything stand out as being out of place?

@@ -156,7 +156,7 @@ public struct IndexPath : ReferenceConvertible, Equatable, Hashable, MutableColl
}
}

mutating func append(contentsOf other: [Int]) {
mutating func append(contentsOf other: __owned [Int]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this probably isn't necessary since it's just [Int]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, Array of Int is not a trivial type, i.e. involves reference counting of the array buffer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but IndexPath doesn't gain anything by taking the array owned, since it's not going to be able to use that buffer for storage, and the type isn't generic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, assuming that the case where self is empty and size of other > 2 is not the common case, it's better to pass other as guaranteed.
(I misread airspeedswift's comment: I though he meant that it's not worth annotating because of the parameter type).

Copy link
Contributor

@jrose-apple jrose-apple left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what the principles are for what is and isn't annotated? Since you're explicitly writing both __shared and __owned, I'd expect it to be

  • every (non-trivial) parameter
  • of all public/@usableFromInline functions and initializers

and perhaps some of the non-public stuff as optimization hints. But I see public functions where that hasn't been done, such as https://github.com/apple/swift/blob/9f042bc7c3452307b7929f5b76db5261b623d79a/stdlib/public/SDK/Foundation/Calendar.swift#L507.

@@ -106,7 +106,7 @@ public struct Calendar : Hashable, Equatable, ReferenceConvertible, _MutableBoxi
// MARK: -
// MARK: Bridging

fileprivate init(reference : NSCalendar) {
fileprivate init(reference : __shared NSCalendar) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should be owned, no? Since you're immediately going to store it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it's not a public entry point anyway, so it doesn't really matter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_MutableHandle makes a copy of the reference given to it so we never store the original — I've propagated __shared out to every init(reference:) which uses this, per @eeckstein's recommendation.

@@ -961,7 +961,7 @@ fileprivate class _JSONReferencingEncoder : _JSONEncoder {

/// Initializes `self` by referencing the given dictionary container in the given encoder.
fileprivate init(referencing encoder: _JSONEncoder,
key: CodingKey, convertedKey: CodingKey, wrapping dictionary: NSMutableDictionary) {
key: CodingKey, convertedKey: __shared CodingKey, wrapping dictionary: NSMutableDictionary) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why only the one parameter changed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter is not stored; all others are. Based on your comments, it sounds to me like I've misunderstood something critical here. Let me start an email thread so we can sort this out.

@@ -156,7 +156,7 @@ public struct IndexPath : ReferenceConvertible, Equatable, Hashable, MutableColl
}
}

mutating func append(contentsOf other: [Int]) {
mutating func append(contentsOf other: __owned [Int]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but IndexPath doesn't gain anything by taking the array owned, since it's not going to be able to use that buffer for storage, and the type isn't generic.

@@ -1178,7 +1178,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - parameter url: The `URL` to read.
/// - parameter options: Options for the read operation. Default value is `[]`.
/// - throws: An error in the Cocoa domain, if `url` cannot be read.
public init(contentsOf url: URL, options: Data.ReadingOptions = []) throws {
public init(contentsOf url: __shared URL, options: __shared Data.ReadingOptions = []) throws {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data.ReadingOptions is an ObjC enum, right? So it shouldn't matter what ownership you use.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure — some of the changes here were mechanical, so I can pull this one out. I'm surprised the compiler didn't produce a warning/error in this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't really talked about what form that'll take yet, but yeah, that's a reasonable idea. (You wouldn't necessarily want to do it to everything that's trivial, because a new version of the library might make it non-trivial, but that's not going to happen to an @objc enum.)

@itaiferber
Copy link
Contributor Author

@jrose-apple Sure — based on the guidelines I was given, I went ahead and annotated every function parameter which deviated from the default rules that I understand are in place (__shared is assumed for all function parameters by default, __owned is assumed for all init parameters by default). Anything not annotated is meant to assume the default.

If it would be more helpful (or even necessary) to annotate all parameters regardless of the default, then I'm willing to go through and do that. Similarly, if I've misunderstood the guidelines (see rdar://problem/34989766 for discussion between between me and @eeckstein), happy to be corrected.

@eeckstein
Copy link
Contributor

@itaiferber

If it would be more helpful (or even necessary) to annotate all parameters regardless of the default

No, that's not necessary

@jrose-apple
Copy link
Contributor

I feel silly now. I didn't realize you were just annotating divergences from the default. Sorry for jumping in!

@itaiferber
Copy link
Contributor Author

@swift-ci Please test macOS

@itaiferber
Copy link
Contributor Author

@swift-ci Please smoke test Linux

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 9f042bc

@itaiferber
Copy link
Contributor Author

Latest commit removes the unnecessary __shared annotations on Data.ReadingOptions and friends (which are Obj-C enums). If tests are green I'll merge.

@itaiferber
Copy link
Contributor Author

@jrose-apple No worries at all, BTW — thanks for taking the time to review! Thanks @eeckstein for the help, too.

@itaiferber itaiferber merged commit a28c9d6 into swiftlang:master Sep 24, 2018
@itaiferber itaiferber deleted the foundation-overlay-owned-annotations branch September 24, 2018 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants