Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-5122] Codable conformance compilation error with required initializers #3849

Closed
hartbit opened this issue Jun 6, 2017 · 14 comments
Closed

Comments

@hartbit
Copy link
Collaborator

hartbit commented Jun 6, 2017

Previous ID SR-5122
Radar rdar://problem/32774777
Original Reporter @hartbit
Type Bug
Status Resolved
Resolution Done
Additional Detail from JIRA
Votes 4
Component/s Compiler, Foundation
Labels Bug
Assignee @itaiferber
Priority Medium

md5: c57b8fae77dfef6e627bfb9f5eb19530

relates to:

  • SR-6875 Codable codegen blocked by subclass with designated initializer

Issue Description:

The following piece of code results in a compilation error, most probably in the code generated by the Codable conformance. This seems to happen when the super class has a required initializer:

class Foo {
    var a: Int

    init() {
        self.a = 0
    }

    required init(a: Int) {
        self.a = a
    }
}

class Bar: Foo, Codable {
    var b: Int

    required init(a: Int) {
        b = 0
        super.init(a: a)
    }
}
@itaiferber
Copy link
Contributor

In the case of classes, we might want to always be willing to call a superclass's default constructor if it's not Codable. Regardless, we need to provide a better diagnostic in this case.

@swift-ci
Copy link
Contributor

Comment by Kashif Shaikh (JIRA)

The same problem can be manifested without required init:

class TestObject {
    let x: String
    
    init(x:String) {
        self.x = x
    }
}

class TestCodable : TestObject, Codable {
    let y: String
    let z: String
    init(x:String, y:String, z:String) {
        self.y = y
        self.z = z
        super.init(x:x)
    }
}

The compiler fails with:

<unknown>:0: error: super.init isn't called on all paths before returning from initializer

If I manually add the decodable methods, it compiles successfully.

class TestCodable : TestObject, Codable {
    let y: String
    let z: String
    init(x:String, y:String, z:String) {
        self.y = y
        self.z = z
        super.init(x:x)
    }
    
    required init(from decoder: Decoder) throws {
        let x = "decode x"
        let y = "decode y"
        let z = "decode z"
        self.y = y
        self.z = z
        super.init(x:x)
    }
}

Because the code generated for the Decodable init method can't know for certain which super.init method to call.

Normally we could code our model objects such that we don't have inheritance, which kind of solves the problem. But many of us are using Realm, and when we add Codable to our Realm model objects, the compiler fails with the same error.

@hartbit
Copy link
Collaborator Author

hartbit commented Jun 12, 2017

I've thought some more about it and I think it would be really beneficial for the compiler to not error out when the super class has an argument-less initialiser. It's going to be such a common case that it would be really a pity to force users to implement their own initializer just to call super.init(). Especially as it's always an option for users to implement it manually when they intend to call a different super initialiser.

@itaiferber
Copy link
Contributor

@swift-ci Create

@mattneub
Copy link

I think the problem can be expressed far more compactly:

class Superclass : Codable {}
class Subclass : Superclass {
    var prop : String?
    init(prop:String) {
        self.prop = prop
    }
}

This gives:

error: 'required' initializer 'init(from:)' must be provided by subclass of 'Superclass'

The problem is that giving the subclass a designated initializer has cut off inheritance of the Codable injected init(from:). We are therefore forced to write init(from:) manually in the subclass. That's ugly.

@itaiferber
Copy link
Contributor

This was actually fixed a while back before the Swift 4.0 release.

@itaiferber
Copy link
Contributor

@mattneub That looks like a separate issue. Let me look into that.

@mattneub
Copy link

@itaiferber Sorry about that: would you like me to submit it separately? I couldn't find anything comparable and this looked very close.

@itaiferber
Copy link
Contributor

@mattneub Yeah, that's not Codable-specific (and is correct behavior AFAICT):

class Super {
    required init() {}
}

class Sub : Super {
    var prop: String?
    init(prop: String) {
        self.prop = prop
    }
}

produces

Untitled 2.swift:10:1: error: 'required' initializer 'init()' must be provided by subclass of 'Super'
}
^
Untitled 2.swift:2:14: note: 'required' initializer is declared in superclass here
    required init() {}

If your superclass has a required initializer, you can only inherit it if you don't add your own initializer. If you remove sub's init(prop: String) it compiles just fine.

@mattneub
Copy link

I know, but this undoes all the good done by Codable's injected init(from:). That's very hard on users (esp. beginners who expect declaring Codable to make all the good stuff happen automagically). I'm asking whether we couldn't somehow stretch a point and make this work.

@itaiferber
Copy link
Contributor

@mattneub That might be possible, but I'm not sure if it would be worth it. Codable just performs codegen; there's nothing it can do that you can't write yourself, and vice versa. Special-casing it through the various levels of the compiler would get real nasty real quick, I think.

That being said, I think that the compiler diagnostic could certainly be improved. Given that it took me a sec to figure out why you were getting that message, I think we can do better (e.g. "note: subclass initializer 'init(prop: String)' prevented inheritance of 'required init()'; 'required init()' must be overridden"). That should be filed separately, though, if you don't mind. 🙂

@mattneub
Copy link

@itaiferber will do.

@itaiferber
Copy link
Contributor

@mattneub Thanks! 🙂

@mattneub
Copy link

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 5, 2022
valeriyvan pushed a commit to valeriyvan/swift-corelibs-foundation that referenced this issue Nov 7, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants