Is there a warning about accidentally overriding a property with a method?

I see quite some code accidentally “override” a property of the superclass using a method, for example:

public class MyViewController: UIViewController {

    public func preferredContentSize() -> CGSize {
        // only for demo purpose
        return CGSize(width: 100, height: 100)
    }

}

Usually such code comes from migrating ObjC code:

@implementation MyViewController

- (CGSize)preferredContentSize {
    // only for demo purpose
    return CGSizeMake(100, 100);
}

@end

It will be great if the compiler could give a warning (could be opt-in) when a subclass defines a method with the same name as a property defined in superclass, as it is unlikly an intended coding practice.

1 Like

It works the other way too. WHY?!

class Super {
  func overloaded() { }
}

final class Sub: Super {
  var overloaded: Void { }
}
let sub = Sub()
let property: () = sub.overloaded
let method: () -> _ = sub.overloaded

The standard library makes use of this form of overloading, for example types conforming to Collection have both a property and a method named first and count.

That is dandy! But not the same. first takes a parameter. I.e. this compiles:

class C {
  var first: Void { }
  func first(_: some Any) { }
}

This does not:

class C {
  var first: Void { }
  func first() { }
}

But split the two things across the subclass boundary, and that compiles too, as we showed.

2 Likes

Good point. This works too though:

class C {
  func x() {}
}

class D: C {
  func x(_: Int) {}
}

Maybe it shouldn't? It's not so clear-cut though.

this behavior is intriguing, and i too am curious to know 'WHY?!' things behave this way... is this just an edge case that has not been considered (and has not clearly caused problems), or is it more intentional? do the vtables not support 'disambiguating' between methods & properties on the same base type or something (a cursory inspection of the SIL seemed like they are at least printed out differently, so maybe are distinguishable)?

as far as diagnosing it goes, that seems like something that'd be fairly straightforward to support as an opt-in diagnostic i'd think... 'just' walk up the inheritance hierarchy for certain overridden decls looking for the right sorts of patterns would perhaps do it (though it might be more complicated than i'm imagining, as things so often are)?


slightly tangential, but here is a further oddity inspired by this:

the shadowing apparently is allowed on the base class if the method & getter are async (which is seemingly not true for other effects, like throws)

class Super {
    func overloaded_async() async { }
    var overloaded_async: Void { get async {} } // ✅ two signatures happily co-existing
}

but if you override via a subclass and don't properly annotate things, you get a SIL verifier error (and maybe a miscompilation?):

final class Sub: Super {
    override var overloaded_async: Void { } // not `async`
}
SIL verification failed: vtable entry for #Super.overloaded_async!getter must be ABI-compatible
  sync/async mismatch
  @convention(method) (@guaranteed Sub) -> ()
  @convention(method) @async (@guaranteed Super) -> ()
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.

reported that one here: SIL verification failure/miscompile when overriding async getter with non-async implementation · Issue #85332 · swiftlang/swift · GitHub

2 Likes