swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.37k stars 10.34k forks source link

[SR-13372] Confusing error messages about property protocol conformance #55812

Open swift-ci opened 4 years ago

swift-ci commented 4 years ago
Previous ID SR-13372
Radar rdar://problem/67360659
Original Reporter vilarneto (JIRA User)
Type Improvement
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Improvement, DiagnosticsQoI, TypeChecker | |Assignee | None | |Priority | Medium | md5: eabb02acaadfb1fdb1dfefdc615deae8

Issue Description:

(Using Swift 5.2.4 under Xcode 11.6)

The following code tries to enforce a behaviour on the child property of the Parent type:

import Foundation

// Identifiable types must have a readable “id” String property

protocol Identifiable {
    var id: String { get }
}

// Basic types

struct Parent: Identifiable {
    let id: String
    let child: Child
}

struct Child: Identifiable {
    let id: String
}

// Now I want to generalise a behaviour on the ‘child’ property

protocol IdentifiableChild {
    var child: Identifiable { get }
}

As everything above compiles cleanly, I didn't expect the following error:

// Compiler error: Type 'Parent' does not conform to protocol 'IdentifiableChild'
// - Detail: Candidate has non-matching type 'Child'
extension Parent: IdentifiableChild { }

After some trial-and-error I understood that IdentifiableChild should be instead declared in one of the following ways:

protocol IdentifiableChild where ChildType: Identifiable {
    associated ChildType
    var child: ChildType { get }
}

…or…

protocol IdentifiableChild {
    associated ChildType: Identifiable
    var child: ChildType { get }
}

However, the compiler error didn't really give me any hints about what was wrong in the first example code, especially given that the original IdentifiableChild definition was accepted without errors or warnings.

The obvious improvement proposal is to rewrite the error message to give the programmer a clearer understanding of the situation and (hopefully) offer a “Fix” action. Moreover, I'd like to ask: Why doesn't the original definition of IdentifiableChild work, and (since it was accepted by the compiler) what is its usefulness?

theblixguy commented 4 years ago

The reason you get an error is because the property requirement in ChildIdentifiable requires its type to be exactly Identifiable, whereas you want something that conforms to Identifiable. This can be expressed by using a constrained associatedtype i.e. associatedtype ChildType: Identifiable.

I suppose we could change the diagnostic note to say candidate has type 'Child' (which conforms to 'Identifiable'), but protocol requires type to be exactly 'Identifiable'. Do you think that would be clearer in this scenario? Also, we could perhaps offer another fix-it to change the requirement's type to use an associated type.

cc @hborla theindigamer (JIRA User) What do you think?

swift-ci commented 4 years ago

Comment by Vilar Fiuza da Camara Neto (JIRA)

@theblixguy The new diagnostic note you propose is much better and also hints about the difference between “being” vs. “conforming” when it comes to protocols – something that beginner-to-intermediate Swift programmers may not be aware of.

Moreover, thanks for the concise and enlightening answer.

typesanitizer commented 4 years ago

Isn't this very much like the type_cannot_conform diagnostic we already have today? It also has an educational note protocol-type-non-conformance.md. Maybe we should reuse that somehow?

theblixguy commented 4 years ago

Yeah, I think we show it when you have T: SomeProtocol requirement somewhere and you pass SomeProtocol however it’s the reverse here (i.e. the requirement is SomeProtocol and you’re passing T where T: SomeProtocol). Also, the diagnostic you mentioned is an error whereas I think we want to use a tailored version of the protocol_witness_type_conflict note.

typesanitizer commented 4 years ago

@swift-ci create