
New Diagnostic Architecture Overview - xedin
https://swift.org/blog/new-diagnostic-arch-overview/
======
saagarjha
This is really great, as Swift’s error messages aren’t great when working with
anything moderately complex. SwiftUI in particular seemed to stretch the
abilities of the type system to the breaking point, and I’m glad to see that
work is being done to improve error messages there instead of failing with
mystifying reasons when people make reasonable mistakes.

~~~
skohan
> SwiftUI in particular seemed to stretch the abilities of the type system to
> the breaking point,

Past the breaking point. SwiftUI brought several new language features just to
make it possible.

------
pohl
Compiler errors in SwiftUI have been especially unhelpful. Is this something
that this effort is intended to improve?

~~~
stirner
I’ve noticed that when editing the ContentView in SwiftUI, errors will
sometimes be attached to a totally unrelated line (OTOH, forgetting to unwrap
an optional returned by NumberFormatter.string() showed

    
    
        type of expression is ambiguous without more context
    

on a straightforward Text() expression halfway up the file). The only way to
find the actual source of the error is to undo every change until the error
disappears, and coupled with sluggish compilation times this becomes a real
pain. I hope they address this issue as part of their improvements.

~~~
msbarnett
This is literally given as an example in the article. No hope required.

~~~
stirner
Oh wow, you’re right, at the very top. Skimmed right over that. Thanks for
pointing it out.

I guess SwiftUI exacerbates the problem since expressions are necessarily very
deeply nested.

~~~
msbarnett
The last example in the SwiftUI section is also an “ambiguous type expression”
example with a fairly complex nested view.

It seems to be a problematic diagnostic they have particularly focused on
resolving.

------
breatheoften
This is an awesome article and great learning opportunity.

The article describes the mental model needed to understand the operation of
the swift type error diagnostic system with a high level of clarity.

Thinking through how this system works provides a super valuable learning
experience for a wide audience of readers.

------
gok
The diagnostic improvements are great. Unfortunately many of these examples
show how serious of a mistake it was to require explicit numeric conversions
all over the place.

------
estebank
I'm really happy to see this effort! I participate in the rustc project
dedicating myself mainly to diagnostics and ergonomics and I believe that the
industry hasn't scratched the surface of what can be done through error
messages. In Rust, my own personal rule of thumb is that the compiler should
be your first tutor and act as a pair programmer. Rust has one advantage for
doing so that was arrived to "by accident": the fewer things are valid code,
the bigger the distance between valid and invalid code. In the boundary of
these two areas there is code that can be reasonably imagined to be valid by
somebody who knows a fraction of the rules of the language. I call this the
"negative space" of the language. You can think of a case (again, in Rust
because it is what I am familiar with, but the ideas can be applied to any
language) like

    
    
        fn main() {
            let x = "a" + "b";
        }
    

which is invalid Rust due to _reasons_ , but the compiler emits the following
output[1]:

    
    
        error[E0369]: binary operation `+` cannot be applied to type `&str`
         --> src/main.rs:2:17
          |
        2 |     let x = "a" + "b";
          |             --- ^ --- &str
          |             |   |
          |             |   `+` cannot be used to concatenate two `&str` strings
          |             &str
        help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
          |
        2 |     let x = "a".to_owned() + "b";
          |             ^^^^^^^^^^^^^^
    

Is it perfect? No, it is verbose and you could make the case that the language
should be extended to accept the code as is. Because we have design
constraints that don't let us accept the code, we've extended _the compiler_
to support the construct in order to provide the fixed code, while
simultaneously allowing the language to conform to its constraints. The hard
part of this is that now your compiler is as complex as it would have to be to
support the superset language. Also, the less negative space there's in the
language, namely, the more expressive it is, the less you can apply this
strategy

I am reminded of a recent tweet: "We spent decades trying to invent a
sufficiently smart compiler when we should have been inventing a sufficiently
empathetic one"[2]

Edit: if you have to Google to understand what the compiler is saying, I
consider that a bug. File tickets for confusing diagnostics!

[1]: [https://play.rust-
lang.org/?version=nightly&mode=debug&editi...](https://play.rust-
lang.org/?version=nightly&mode=debug&edition=2018&gist=10cdf4ca498752f184e1998dac97b566)

[2]:
[https://twitter.com/mostlysafe/status/1176170363266449410](https://twitter.com/mostlysafe/status/1176170363266449410)

~~~
xedin
There is a discussion about introducing "descriptive" diagnostics of the same
style happening on the Swift Forums at the moment -
[https://forums.swift.org/t/pitch-extended-error-messages-
dia...](https://forums.swift.org/t/pitch-extended-error-messages-diagnostic-
naming/28754).

I this it's a great idea and with new diagnostic architecture described in the
blog post it would be relatively easy to archive.

~~~
xedin
This is the diagnostic produced for similar example using new diagnostic
architecture:

error: binary operator '+' cannot be applied to operands of type 'String' and
'Int'

_ = "a" \+ 1

    
    
       ~~~ ^ ~
    

note: overloads for '+' exist with these partially matching parameter lists:
(Int, Int), (String, String)

_ = "a" \+ 1

    
    
           ^
    

note: candidate expects value of type 'String' for parameter #2

    
    
        @inlinable public static func + (lhs: String, rhs: String) -> String
                                      ^
    

note: candidate expects value of type 'Int' for parameter #1

    
    
        public static func + (lhs: Int, rhs: Int) -> Int
                           ^

