[swift-evolution] Proposal: Add @requires_super attribute

Marc Knaup marc at knaup.koeln
Wed Dec 16 14:07:14 CST 2015


This fits with the approach that "required" will only check that the call
to the superclass' implementation is present but not that it is actually
called.

OTOH should it be required to call the super implementation exactly once no
matter what codepath is taken then throwing should be allowed before
super's implementation is called.
It may be helpful though to check a few examples to better evaluate that
case.

On Wed, Dec 16, 2015 at 8:55 PM, Ian Ynda-Hummel <ianynda at gmail.com> wrote:

> Yes, I forgot the func. I can type, I swear. :)
>
> As for the throws thing, which it doesn't look like has been addressed:
> method overrides cannot throw unless the overridden method throws, and it
> seems like in the case that an error is thrown it doesn't matter if it the
> was the superclass or the subclass as execution of the method is halted
> anyway. I might be missing something, but I don't currently see a reason
> that required (or whatever keyword) would exclude throws.
>
> On Wed, Dec 16, 2015 at 2:36 PM Marc Knaup <marc at knaup.koeln> wrote:
>
>> PS: Just swap "init()" for "func foo()" and you have the syntax for
>> functions which require super calls in subclasses :)
>>
>>
>> On Wed, Dec 16, 2015 at 8:34 PM, Marc Knaup <marc at knaup.koeln> wrote:
>>
>>> I assume you just forgot "func".
>>>
>>> Okay, in the case where the super implementation is not "required" but
>>> the subclass starts using "required" you must specify both.
>>>
>>> This is the same as the current behavior for initializers:
>>>
>>> class A {
>>> init() {}
>>> }
>>>
>>> class B: A {
>>> required override init() {}
>>> }
>>>
>>> class C: B {
>>> required init() {}
>>> }
>>>
>>>
>>> Btw I just noticed that my previous assumption was wrong. Subclasses can
>>> always use "override" and "required". For class "C" it would just yield a
>>> warning because it's redundant.
>>>
>>> On Wed, Dec 16, 2015 at 8:29 PM, Ian Ynda-Hummel <ianynda at gmail.com>
>>> wrote:
>>>
>>>> Well, consider the following:
>>>>
>>>>     class Foo {
>>>>         func foo() {
>>>>
>>>>         }
>>>>     }
>>>>
>>>>     class Bar: Foo {
>>>>         override required foo() {
>>>>
>>>>         }
>>>>     }
>>>>
>>>>     class Baz: Bar {
>>>>         override(?) required foo() {
>>>>
>>>>         }
>>>>     }
>>>>
>>>> The override would be required for Bar. If not it's either making
>>>> assumptions about the superclass that may not be correct or no subclass can
>>>> make an overridden method required by its subsequent subclasses.
>>>> Initializers have the convenience of always being required for the root
>>>> class.
>>>>
>>>> Now I think I'm talking myself out of the initializer behavior being
>>>> correct. I'll just call myself undecided about that for now.
>>>>
>>>>
>>>> On Wed, Dec 16, 2015 at 2:22 PM Marc Knaup <marc at knaup.koeln> wrote:
>>>>
>>>>> When requiring super's implementation to always be called Swift should
>>>>> not make the assumption that two methods with the same name but different
>>>>> signatures are interchangeable.
>>>>> Only the super method with exactly the same signature - i.e. the
>>>>> overridden one - should be allowed to called. Anything else will likely be
>>>>> complex and confusing.
>>>>>
>>>>> @Ian "override" keyword is neither necessary nor allowed when
>>>>> "required" is used since it implies exactly that already.
>>>>>
>>>>> On Wed, Dec 16, 2015 at 8:17 PM, Ian Ynda-Hummel via swift-evolution <
>>>>> swift-evolution at swift.org> wrote:
>>>>>
>>>>>> @Jordan I might slowly be convincing myself that initialization
>>>>>> behavior is correct, though. For example, this feels right:
>>>>>>
>>>>>>     class Foo {
>>>>>>         required func foo() {
>>>>>>
>>>>>>         }
>>>>>>     }
>>>>>>
>>>>>>     class Bar: Foo {
>>>>>>         required func foo(bar: Bar) {
>>>>>>             super.foo()
>>>>>>         }
>>>>>>     }
>>>>>>
>>>>>> This, however, feels less right:
>>>>>>
>>>>>>     class Foo {
>>>>>>         required func foo(string: String) {
>>>>>>
>>>>>>         }
>>>>>>
>>>>>>         required func foo(url: NSURL) {
>>>>>>
>>>>>>         }
>>>>>>     }
>>>>>>
>>>>>>     class Bar: Foo {
>>>>>>         override required func foo(string: String) {
>>>>>>             super.foo(url: NSURL(string: string)!)
>>>>>>         }
>>>>>>     }
>>>>>>
>>>>>> But regardless, I'm having trouble coming up with a better keyword.
>>>>>>
>>>>>> fo
>>>>>> On Wed, Dec 16, 2015 at 2:03 PM T.J. Usiyan <griotspeak at gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> +1
>>>>>>>
>>>>>>> I thought that there must have been some reason not to include it
>>>>>>> that I hadn't considered when it wasn't in Swift 1.0.
>>>>>>>
>>>>>>> On Wed, Dec 16, 2015 at 1:59 PM, Jordan Rose via swift-evolution <
>>>>>>> swift-evolution at swift.org> wrote:
>>>>>>>
>>>>>>>> My hesitation about "required" is that required initializers don't
>>>>>>>> have to call the *same* initializer, which is something that
>>>>>>>> NS_REQUIRES_SUPER *does* enforce. (In fact, this new attribute
>>>>>>>> could very well apply to required initializers as well: if you implement
>>>>>>>> this initializer, you must chain to the same initializer. I'm not quite
>>>>>>>> sure when that would come up, but it's potentially useful.)
>>>>>>>>
>>>>>>>> Jordan
>>>>>>>>
>>>>>>>> On Dec 16, 2015, at 10:56 , Ian Ynda-Hummel via swift-evolution <
>>>>>>>> swift-evolution at swift.org> wrote:
>>>>>>>>
>>>>>>>> +1 for this if for nothing else but UIKit classes yelling at me to
>>>>>>>> call super.viewDidLoad().
>>>>>>>>
>>>>>>>> I think using the required keyword makes sense. The one possible
>>>>>>>> caveat is overloading, as my knee jerk reaction is that methods of the same
>>>>>>>> name would behave like initializers. So a method overriding a required
>>>>>>>> method would have to call a required method of the same name on the
>>>>>>>> superclass if one exists. I'm not convinced that's correct, though.
>>>>>>>>
>>>>>>>> On Wed, Dec 16, 2015 at 1:40 PM Marc Knaup via swift-evolution <
>>>>>>>> swift-evolution at swift.org> wrote:
>>>>>>>>
>>>>>>>>> "required" also doesn't mean that a subclass has to implement the
>>>>>>>>> required initializer since it can be inherited.
>>>>>>>>> Your example is an abstract function which should have it's own
>>>>>>>>> keyword (if we ever get abstract functions).
>>>>>>>>>
>>>>>>>>> On Wed, Dec 16, 2015 at 7:37 PM, Vester Gottfried <
>>>>>>>>> vester.gottfried at gmail.com> wrote:
>>>>>>>>>
>>>>>>>>>> I think reusing required would send the wrong message. Required
>>>>>>>>>> would mean for me something like NSOperation subclasses maybe require to
>>>>>>>>>> have a main() function, but that doesn't mean you have to call super. On
>>>>>>>>>> the contrary, the documentation of NSOperation main() explicitly states not
>>>>>>>>>> to call super.
>>>>>>>>>>
>>>>>>>>>> On Wed, Dec 16, 2015 at 7:08 PM, Marc Knaup <marc at knaup.koeln>
>>>>>>>>>> wrote:
>>>>>>>>>>
>>>>>>>>>>> What about re-using the "required" keyword in the superclass
>>>>>>>>>>> which already means something similar for initializers?
>>>>>>>>>>> Subclass implementations are required to call super's
>>>>>>>>>>> implementation.
>>>>>>>>>>> If a subclass doesn't implemented the required method it could
>>>>>>>>>>> mean that it inherits the behavior from the superclass - just like
>>>>>>>>>>> initializers can be inherited too.
>>>>>>>>>>>
>>>>>>>>>>> On Wed, Dec 16, 2015 at 7:02 PM, Jordan Rose <
>>>>>>>>>>> jordan_rose at apple.com> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> +1 from me. FWIW, the Objective-C one is syntactic.
>>>>>>>>>>>>
>>>>>>>>>>>> Information from Radar: the request for this is
>>>>>>>>>>>> rdar://problem/17408107 (plus a few duplicates). One of the
>>>>>>>>>>>> dups suggests a variation where a subclass method can be declared as
>>>>>>>>>>>> "refine" instead of "override" so that you can document that your
>>>>>>>>>>>> *own* method is expected to call super. In this model,
>>>>>>>>>>>> "@requires_super" could become something like "imposed". I personally think
>>>>>>>>>>>> this doesn't add enough, especially since we wouldn't be publishing
>>>>>>>>>>>> refine-vs-override in a library's public interface.
>>>>>>>>>>>>
>>>>>>>>>>>> Jordan
>>>>>>>>>>>>
>>>>>>>>>>>> On Dec 16, 2015, at 9:49 , Marc Knaup via swift-evolution <
>>>>>>>>>>>> swift-evolution at swift.org> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>> Sounds reasonable since even the best flow analysis cannot
>>>>>>>>>>>> ensure that all codepaths call the super implementation.
>>>>>>>>>>>>
>>>>>>>>>>>> Some more edge cases:
>>>>>>>>>>>>
>>>>>>>>>>>>    - Calling super asynchronously by using it in a closure
>>>>>>>>>>>>    - Referring to the super implementation by assign it to a
>>>>>>>>>>>>    variable and call it later (is that really possible? never did that)
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> On Wed, Dec 16, 2015 at 6:25 PM, Vester Gottfried <
>>>>>>>>>>>> vester.gottfried at gmail.com> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> I would suggest that @requires_super only checks if a call to
>>>>>>>>>>>>> super is present at all. More detailed behaviour should be part of the
>>>>>>>>>>>>> functions documentation, because I think all possibilities cannot be
>>>>>>>>>>>>> checked easily by the compiler. For example a call to super my be required
>>>>>>>>>>>>> to happen early or late inside the function. But when too early or too late
>>>>>>>>>>>>> is can probably not been forseen by the compiler.
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Wed, Dec 16, 2015 at 5:46 PM, Marc Knaup <marc at knaup.koeln>
>>>>>>>>>>>>>  wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> +1 always had such issues with UIViewController's lifecycle
>>>>>>>>>>>>>> methods.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> But edge cases need to be considered like "throws" for
>>>>>>>>>>>>>> example.
>>>>>>>>>>>>>> Do I need to call super before I throw something?
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On Wed, Dec 16, 2015 at 5:41 PM, Matthew Johnson via
>>>>>>>>>>>>>> swift-evolution <swift-evolution at swift.org> wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> +1 to this.  Anything that helps ensure inheritance is
>>>>>>>>>>>>>>> thought through carefully and used correctly is a win.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> On Dec 16, 2015, at 10:32 AM, Vester Gottfried via
>>>>>>>>>>>>>>> swift-evolution <swift-evolution at swift.org> wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Some class based libraries/frameworks expect the consumer to
>>>>>>>>>>>>>>> subclass certain classes and override specific method and require that the
>>>>>>>>>>>>>>> super implementation of an overridden method is being called.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Not calling the super implementation is a common source of
>>>>>>>>>>>>>>> bugs that may be prevented if the compiler checks if super is called, like
>>>>>>>>>>>>>>> it does in some cases of init().
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Example:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> class Box {
>>>>>>>>>>>>>>>    @requires_super
>>>>>>>>>>>>>>>     func addStuff() { ... }
>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Overriding class Box's addStuff without calling
>>>>>>>>>>>>>>> super.addStuff() should result in an error
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> class Chest : Box {
>>>>>>>>>>>>>>>     override addStuff() {
>>>>>>>>>>>>>>>          // ERROR: addStuff() requires call to
>>>>>>>>>>>>>>> super.addStuff()
>>>>>>>>>>>>>>>         ...
>>>>>>>>>>>>>>>     }
>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Objective-C developers know this as NS_REQUIRES_SUPER and I
>>>>>>>>>>>>>>> think its worth thinking about adapting it.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I hope my proposal was clear and thanks for reading,
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Gottfried
>>>>>>>>>>>>>>>  _______________________________________________
>>>>>>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>>>>>>> swift-evolution at swift.org
>>>>>>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> _______________________________________________
>>>>>>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>>>>>>> swift-evolution at swift.org
>>>>>>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>  _______________________________________________
>>>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>>>> swift-evolution at swift.org
>>>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>> _______________________________________________
>>>>>>>>> swift-evolution mailing list
>>>>>>>>> swift-evolution at swift.org
>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>>> swift-evolution mailing list
>>>>>>>> swift-evolution at swift.org
>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>>> swift-evolution mailing list
>>>>>>>> swift-evolution at swift.org
>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>> _______________________________________________
>>>>>> 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/20151216/e1b7ba0c/attachment.html>


More information about the swift-evolution mailing list