Overriding Convenience Initializers (Possible Bug?)

I’m building a linux Path library that wraps the Glibc APIs (rather than using Foundation.FileManager which isn’t fully implemented on Linux).

I’m receiving an error I don’t understand though:

public class AbsolutePath: RelativePath {
    // cannot override non-throwing initializer with throwing initializer
    public convenience init(_ path: Path) throws {
        self.init()
        self.path = try AbsolutePath.expand(path)
    }

The parent class RelativePath has the following initializer which is causing the collision:

public class RelativePath: Path {
    public convenience init(_ path: Path) {
        self.init(path.string)
    }
}

I don’t understand why this is causing the collision though. Aren’t convenience initializers specific to that class? They can’t be overridden in subclasses anyways so why can’t I write a new convenience initializer with a different signature?

The root Path class has the following initializer:

public class Path {
    public convenience init(_ path) {
        self.init(path.string)
    }
}

I don’t get an error in the RelativePath when I declare the convenience initializer with the same signature. I don’t have to use the override keyword because I’m not overriding anything since they’re all convenience initializers.

If I add the override keyword to my convenience initializer I receive an error that I would expect to see:

public class AbsolutePath: RelativePath {
    // initializer does not override a designated initializer from its superclass
    public override convenience init(_ path: Path) throws {
        self.init()
        self.path = try AbsolutePath.expand(path)
    }

I’ve looked through Apple’s documentation about initializers (specifically the section titled “Class Inheritance and Initialization”) and I can’t seem to find any reasonable explanation for why I would be receiving this sort of collision when I’m using convenience initializers.

It would make sense if they were designated initializers, and that’s actually why I switched to convenience initializers in the first place! I wanted to override in AbsolutePath with throwable initializers but didn’t want/need to make the RelativePath and Path ones also throwable.

Does anyone know or understand why this is happening? Or is this a Swift bug?

Very interesting. Looks like you did something the compiler didn’t expect. You didn’t override any designated initializers in AbsolutePath, so the convenience initializers got inherited, which likely is the reason there is a conflict between the throwing and non-throwing one which otherwise have identical signatures.

@Ponyboy47 I realized that a throwable and a non-throwable initializer with equal signatures will conflict. Because your convenience initializer is inherited, it conflicts with the new throwable one, which makes the compiler think you want to “override” it. It remains unclear though why can’t a non-throwable convenience initialize be substituted with a throwable one with the same signature. Has been already filed last summer: SR-5355

Remember that convenience initializers are inherited if you implement all of the designated initializers from your superclass, or if you don’t provide any designated initializers (in which case you inherit all initializers from your superclass).

Problem 1: there is currently no way to stop this from happening.

I would also not be surprised if we have an actual bug where we think you wanted to override something even though you just meant to define a new initializer with the same name (as you described). That’s problem 2.

You can sometimes work around these problems by putting another class between AbsolutePath and RelativePath that’s only there to keep from inheriting initializers. It’s ugly, but it works.

Please feel free to file bugs for either of these problems if you have a complete self-contained test case you can share!

Ironically, I seem to have found an autoComplete imperfection again. It doesn’t show the convenience initializers if they were inherited. Going to file that in a moment.

edit: SR-7256

Thank you all for your suggestions and input! I’ve definitely learned some new things and should at least be able to work around my issues now