swiftlang / swift

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

Failed to produce diagnostic when unable to choose correct initializer inside of result builder #65467

Open j-f1 opened 1 year ago

j-f1 commented 1 year ago

Description When you have (1) a key path to a value of type A, passed to (2) a struct initializer with two overloads which expect the key path to be either of type B or C, (3) inside of a result builder, Swift reports “failed to produce diagnostic for expression; please submit a bug report” instead of a useful error.

Steps to reproduce

struct Model {
    let member = ""
}

struct Wrapper {
    // produces correct error message if one of these is removed
    init(keyPath: KeyPath<Model, Int32>) {}
    init(keyPath: KeyPath<Model, Float>) {}
}

// can be any result builder
@resultBuilder struct MyResultBuilder {
    static func buildBlock(_ components: Void...) -> Void {}
}

// 🛑 Type of expression is ambiguous without more context
let baz = Wrapper(keyPath: \.member)

@MyResultBuilder
// 🛑 Failed to produce diagnostic for expression
var built: Void {
    let baz = Wrapper(keyPath: \.member)
}

Expected behavior

error: TypeConfusion.playground:18:15: error: no exact matches in call to initializer 
    let baz = Wrapper(keyPath: \.member)
              ^

TypeConfusion.playground:7:5: note: candidate expects value of type 'KeyPath<Model, Int32>' for parameter #1 (got 'KeyPath<Model, String>')
    init(keyPath: KeyPath<Model, Int32>) {}
    ^

TypeConfusion.playground:8:5: note: candidate expects value of type 'KeyPath<Model, Float>' for parameter #1 (got 'KeyPath<Model, String>')
    init(keyPath: KeyPath<Model, Float>) {}

Environment

varunseth578 commented 1 year ago

let baz = Wrapper(keyPath: \Model.member) // specify the type of keyPath as KeyPath<Model, String>

AnthonyLatsis commented 1 year ago

Slightly simpler:

struct Model {
  let member: String
}

func foo(_: KeyPath<Model, Int32>) {}
func foo(_: KeyPath<Model, Float>) {}

@resultBuilder
struct MyResultBuilder {
  static func buildBlock() -> Void {}
}

foo(\.member)

@MyResultBuilder
var built: Void {
  foo(\.member)
}
xedin commented 1 year ago

@amritpan is going to look into this one. Note that if we fix standalone foo(\.member) the result builder case would be fixed as well.