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

Reply via email to