[swift-evolution] [Pitch] Make the formal type of 'self' consistent in class methods

Matthew Johnson matthew at anandabits.com
Thu Jun 23 15:25:52 CDT 2016



Sent from my iPad

> On Jun 23, 2016, at 3:12 PM, Slava Pestov <spestov at apple.com> wrote:
> 
> Great to hear some feedback so quickly, especially about something so mundane.

You caught me at the right time I guess.  :)  

This kind of thing is only mundane until your code doesn't behave as you expect.  Then it can become downright maddening!  I would never in a million years guess that the return type would affect the interpretation of self in this way.

BTW, allowing Self to appear in covarying positions in argument types and the protocol conformances this will enable are a very nice bonus (even if it doesn't happen right away).

> 
> I suspect the real reason it doesn’t work this way now is that ‘Self’ is not fully plumbed through. In particular, if a closure captures the ‘Self’ type, IRGen does not properly codegen it, causing compile-time or run-time crashes:
> 
> class MyClass {
> 	func foo(x: Int) -> Self {
> 
> 		// Crash!
> 		_ = { print(self); print(x) }
> 
> 		return self
> 	}
> 
> 	func bar(x: Int) -> MyClass {
> 
> 		// OK!
> 		_ = { print(self); print(x) }
> 
> 		return self
> 	}
> }
> 
> Assertion failed: (LocalSelf && "no local self metadata"), function getLocalSelfMetadata, file /Users/slava/new/swift/lib/IRGen/GenType.cpp, line 1805.
> 0  swift                    0x0000000114d5276e llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 46
> 1  swift                    0x0000000114d52c99 PrintStackTraceSignalHandler(void*) + 25
> 2  swift                    0x0000000114d4efc9 llvm::sys::RunSignalHandlers() + 425
> 3  swift                    0x0000000114d53312 SignalHandler(int) + 354
> 4  libsystem_platform.dylib 0x00007fffc438a01a _sigtramp + 26
> 5  libsystem_platform.dylib 0x00000000507ca710 _sigtramp + 2353268496
> 6  swift                    0x0000000114d52cbb raise + 27
> 7  swift                    0x0000000114d52d62 abort + 18
> 8  swift                    0x0000000114d52d4e __assert_rtn + 126
> 9  swift                    0x000000010f6e8524 swift::irgen::IRGenFunction::getLocalSelfMetadata() + 100
> 
> This comes up most frequently with the ‘weak self / strong self’ dance.
> 
> I’m going to fix this bug really soon, and it seems logical to deal with the language wart as well. We need the IRGen fix for SE-0086 as well in any case.
> 
> Slava
> 
>> On Jun 23, 2016, at 1:08 PM, Matthew Johnson <matthew at anandabits.com> wrote:
>> 
>> +1.  I have not encountered this issue myself but it looks like something that would cause a lot of head scratching if I had.  It is also something that I am unlikely to remember immediately if I run into it in the future.  The current behavior appears broken to me.  It will be great to have it fixed.
>> 
>>> On Jun 23, 2016, at 2:53 PM, Slava Pestov via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>> Consistent formal type for 'self' in class methods
>>> Proposal: SE-9999
>>> Author: Slava Pestov
>>> Status: Awaiting review
>>> Review manager: TBD
>>> Introduction
>>> 
>>> This proposal makes the self value behave consistently whether or not it is used from a method with a Self return type.
>>> 
>>> Swift-evolution thread: Discussion thread topic for that proposal
>>> 
>>> Motivation
>>> 
>>> Right now, we exhibit inconsistent behavior when self is used as an argument to a generic function, violating the principle of least surprise.
>>> 
>>> Consider the following code:
>>> 
>>> class Base {
>>>   @discardableResult
>>>   func methodWithDynamicSelf() -> Self {
>>>     doSomething(self)
>>>     return self
>>>   }
>>> 
>>>   func methodWithoutDynamicSelf() {
>>>     doSomething(self)
>>>   }
>>> }
>>> 
>>> class Derived : Base {}
>>> 
>>> func doSomething<T>(_ t: T) {
>>>   print(T.self)
>>> }
>>> 
>>> Base().methodWithDynamicSelf()
>>> Base().methodWithoutDynamicSelf()
>>> 
>>> Derived().methodWithDynamicSelf()
>>> Derived().methodWithoutDynamicSelf()
>>> Currently, it prints the following output:
>>> 
>>> Base
>>> Base
>>> Derived
>>> Base
>>> Note that there's no inconsistency when the method is called on the base class. When called on the derived class however, we see that in a method with a dynamic Self return type, the type of self is Derived, whereas in a method with any other return type, the type of self is Base.
>>> 
>>> Proposed solution
>>> 
>>> The proposal is to change the type of self to always be Self, which can be thought of as a special generic type parameter bound to the dynamic type of the instance.
>>> 
>>> With this proposal, the above code will instead produce the following:
>>> 
>>> Base
>>> Base
>>> Derived
>>> Derived
>>> Here, the type of self would always be Derived when called on an instance of the derived class.
>>> 
>>> Of course a more useful program could instead do something with the type parameter T, such as constraining it to a protocol or a class with a required initializer, and then using the type to construct a new instance of the class.
>>> 
>>> This also dovetails nicely with SE-0068.
>>> 
>>> Finally, it opens the door to generalizing dynamic Self, allowing it to appear in covariant position within parameter types:
>>> 
>>> class ArtClass {
>>>   func paint(withBrush: (Self) -> ()) { ... }
>>> }
>>> This would allow a class to conform to a protocol with a requirement written like the following, something that is currently not possible at all:
>>> 
>>> protocol OddProtocol {
>>>   func weaken<X, Y>((Self) -> (X) -> Y) -> (X) -> Y
>>> }
>>> Detailed design
>>> 
>>> There's really not much more to say here. The code for typing self with a dynamic Self is in place already, however enabling this change might expose some new bugs we have not yet encountered, because currently, methods with dynamic Self return type are relatively rare.
>>> 
>>> Impact on existing code
>>> 
>>> This will have a small impact on existing code that uses a pattern similar to the above.
>>> 
>>> Alternatives considered
>>> 
>>> One alternative is to simply do nothing, but this makes the language less consistent than it could be.
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160623/71c13406/attachment.html>


More information about the swift-evolution mailing list