Hi Alex, Probably not, The name is a strawman. I guess I could not resist the optimizer speak after all. The programmer is asserting he guarantees the lifetime by other means. I expect if this proposal moves forward the standard library team will decide on the right name for this method.
assertLifetimeGuarantee, withAssertedLifetime, unsafePassWithAssertedLifetime, ... ? > On Mar 15, 2016, at 6:14 PM, Alex Migicovsky <[email protected]> wrote: > > Hi Arnold, > > This seems like a great optimization tool. The only question I have is about > the method name. With the name `withUnsafeGuaranteedValue` we’re moving the > term “guaranteed” into API in addition to it being an implementation detail > of the compiler as far as I could tell. Is “guaranteed" the best name for > this? > > - Alex > >> On Mar 15, 2016, at 6:01 PM, Arnold Schwaighofer via swift-evolution >> <[email protected]> wrote: >> >> >> Updated proposal: use a method that accepts a closure to delineate the >> required guaranteed lifetime for which the assertion by the programmer holds. >> And, hopefully, no optimizer language speak. >> >> >> Add an API to Unmanaged to get the instance it holds @guaranteed >> Proposal: SE-0000 Add an API to Unmanaged to get the instance it holds >> guaranteed >> Author(s): Arnold Schwaighofer >> Status: Draft >> Review manager: TBD >> Introduction >> >> The standard library Unmanged<Instance> struct provides an instance wrapper >> that does not participate in ARC; it allows the user to make manual >> retain/release calls and get at the contained instance at >> balanced/unbalanced retain counts. >> >> This proposal suggests to add another method withUnsafeGuaranteedValue to >> Unmanaged that accepts a closure. Calling this method is akin to making an >> assertion about the guaranteed lifetime of the instance for the delinated >> scope of the method invocation. The closure is passed the unmanaged >> reference as a managed reference. >> >> func doSomething(u : Unmanged<Owned>) { >> // The programmer asserts that there exists another managed reference of >> the >> // unmanaged reference stored in 'u' and that the lifetime of the >> referenced >> // instance is guaranteed to extend beyond the >> 'withUnsafeGuaranteedValue' >> // invocation. >> u.withUnsafeGuaranteedValue { >> $0.doSomething() >> } >> } >> This assertion will help the compiler remove ARC operations. >> >> public struct Unmanaged<Instance : AnyObject> { >> >> // Get the value of the unmanaged referenced as a managed reference without >> // consuming an unbalanced retain of it and pass it to the closure. Asserts >> // that there is some other reference ('the owning reference') to the >> // unmanaged reference that guarantees the lifetime of the unmanaged >> reference >> // for the duration of the 'withUnsafeGuaranteedValue' call. >> // >> // NOTE: You are responsible for ensuring this by making the owning >> reference's >> // lifetime fixed for the duration of the 'withUnsafeGuaranteedValue' call. >> // >> // Violation of this will incur undefined behavior. >> // >> // A lifetime of a reference 'the instance' is fixed over a point in the >> // programm if: >> // >> // * There is another managed reference to the same instance 'the instance' >> // whose life time is fixed over the point in the program by means of >> // 'withExtendedLifetime' closing over this point. >> // >> // var owningReference = Instance() >> // ... >> // withExtendedLifetime(owningReference) { >> // point($0) >> // } >> // >> // Or if: >> // >> // * There is a class, or struct instance ('owner') whose lifetime is >> fixed at >> // the point and which has a stored property that references 'the >> instance' >> // for the duration of the fixed lifetime of the 'owner'. >> // >> // class Owned { >> // } >> // >> // class Owner { >> // final var owned : Owned >> // >> // func foo() { >> // withExtendedLifetime(self) { >> // doSomething(...) >> // } // Assuming: No stores to owned occur for the dynamic lifetime >> of >> // // the withExtendedLifetime invocation. >> // } >> // >> // func doSomething() { >> // // both 'self' and 'owned''s lifetime is fixed over this point. >> // point(self, owned) >> // } >> // } >> // >> // The last rule applies transitively through a chains of stored references >> // and nested structs. >> // >> // Examples: >> // >> // var owningReference = Instance() >> // ... >> // withExtendedLifetime(owningReference) { >> // let u = Unmanaged.passUnretained(owningReference) >> // for i in 0 ..< 100 { >> // u.withUnsafeGuaranteedValue { >> // $0.doSomething() >> // } >> // } >> // } >> // >> // class Owner { >> // final var owned : Owned >> // >> // func foo() { >> // withExtendedLifetime(self) { >> // doSomething(Unmanaged.passUnretained(owned)) >> // } >> // } >> // >> // func doSomething(u : Unmanged<Owned>) { >> // u.withUnsafeGuaranteedValue { >> // $0.doSomething() >> // } >> // } >> // } >> public func withUnsafeGuaranteedValue<Result>( >> @noescape closure: (Instance) throws -> Result >> ) rethrows { >> let instance = _value >> let (guaranteedInstance, token) = Builtin.unsafeGuaranteed(instance) >> try closure(guaranteedInstance) >> Builtin.unsafeGuaranteedEnd(token) >> } >> Prototype: link to a prototype implementation >> >> Motivation >> >> A user can often make assertions that an instance is kept alive by another >> reference to it for the duration of some scope. >> >> Consider the following example: >> >> class Owned { >> func doSomeWork() {} >> } >> >> public class Owner { >> var ref: Owned >> >> init() { ref = Owned() } >> >> public func doWork() { >> withExtendedLifetime(self) { >> doSomething(ref) >> } >> } >> >> func doSomething(o: Owned) { >> o.doSomeWork() >> } >> } >> In this context the lifetime of Owner always exceeds o: Ownee. However, >> there is currently no way to tell the compiler about such an assertion. We >> can pass reference counted values in an Unmanged container without incurring >> reference count changes. >> >> public class Owner { >> >> public func doWork() { >> // No reference count for passing the parameter. >> doSomething(Unmanaged.passUnretained(ref)) >> } >> >> func doSomething(u: Unmanaged<Owned>) { >> // ... >> } >> } >> We can get at the contained instance by means of takeUnretainedValue() which >> will return a balanced retained value: the value is returned at +1 for >> release by the caller. However, when it comes to accessing the contained >> instance we incur reference counting. >> >> func doSomething(u: Unmanaged<Owned>) { >> // Incurs refcount increment before the call and decrement after for >> self. >> u.takeUnretainedValue().doSomeWork() >> } >> With the proposed API call the user could make the assertion that u's >> contained instance's lifetime is guaranteed by another reference to it. >> >> func doSomething(u: Unmanaged<Owned>) { >> // Incurs refcount increment before the call and decrement after for self >> // that can be removed by the compiler based on the assertion made. >> u.withUnsafeGuaranteedValue { >> $0.doSomeWork() >> } >> } >> The compiler can easily remove the reference counts marked by this method >> call with local reasoning. >> >> Impact on existing code >> >> This is a new API that does not replace an existing method. No existing code >> should be affected. >> >> Alternatives considered >> >> A somewhat related proposal would be to allow for class types to completely >> opt out of ARC forcing the programmer to perform manual reference counting. >> The scope of such a proposal would be much bigger making it questionable for >> Swift 3 inclusion. I believe that the two approaches are complementary and >> don't completely supplant each other. This proposal's approach allows for >> selectively opting out of ARC while providing a high notational burdon when >> wide spread over the application (wrapping and unwrapping of Unmanaged). >> Opting completely out of ARC is an all-in solution, which on the other hand >> does not have the high notational burden. >> >> >>> On Mar 15, 2016, at 4:06 PM, Dave Abrahams <[email protected]> wrote: >>> >>> >>> on Mon Mar 14 2016, John McCall <rjmccall-AT-apple.com> wrote: >>> >>>>> In this example this would be for the lifetime for the value in x which >>>>> extends to the lifetime of y. >>>> >>>> What if I return it or assign it to non-local memory? >>>> >>>> I feel like you’re trying to define this by optimizer behavior. >>>> That’s not a workable language rule; programmers are not going to >>>> reason about SSA values. >>> >>> Thank you, John. That is the problem I usually have with evaluating >>> most optimizer proposals. >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] >> https://lists.swift.org/mailman/listinfo/swift-evolution >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
