Update on experimental diagnostics features

Hi all,

Since it's been awhile, I wanted to give an update on the progress of experimental diagnostics-related features as described here and solicit some community feedback before moving towards making them "officially supported". Everything I've described here is currently implemented in swift.org master snapshots behind frontend flags (which are subject to change without notice). I've included a list of open questions that I think deserve additional discussion before shipping these features, along with some notes about what is in progress or still needs to be implemented. Any and all feedback is welcome! I'd appreciate hearing what people think works well, what doesn't, and any suggestions for improvement.

Educational Notes (enable using -Xfrontend -enable-educational-notes in a recent master snapshot)

Educational notes are intended to supplement error messages with focused educational documentation content. The idea behind this feature is that by providing this additional resource, we can turn errors into an opportunity to improve users' understanding of language concepts.

As an example, consider the well-known error protocol can only be used as a generic constraint because it has Self or associated type requirements. A search for this message on stack overflow returns over 300 relevant questions, indicating it alone isn't enough to help users understand what they're doing wrong when they encounter it. This makes sense; protocol existentials are difficult to understand, especially for beginners, and a single line can't hope to adequately explain the relevant limitations. When educational notes are enabled, the following supplementary information is provided alongside the regular message:


Using Protocols with Self or Associated Type Requirements

Protocols in Swift may be used as types, as part of a generic constraint, or as part of an opaque result type.

// CustomStringConvertible can be used as a type.
func foo(bar: CustomStringConvertible) { /* ... */ }

// ...or as a generic constraint on 'T'.
func bar<T: CustomStringConvertible>(baz: T) { /* ... */ }

// ...or as part of an opaque result type.
func baz() -> some CustomStringConvertible { /* ... */ }

While all Swift protocols can be used as generic constraints and as part of opaque result types, not all protocols can be used as types in general. Specifically, if a protocol has a requirement which references Self or an associated type, it cannot be used as a type. One such protocol is Identifiable, which has the requirement var id: ID { get }, where ID is an associated type. As a result, the following code is not allowed:

func foo(bar: Identifiable) { /* ... */ }
// error: protocol 'Identifiable' can only be used as a generic constraint because it has Self or associated type requirements

Protocols like Identifiable which have Self or associated type requirements cannot be used as types because such types would rarely be useful in practice. They would be unable to allow use of Self or associated type requirements like var id: ID { get } because the associated type is not specified.

When working with protocols having Self or associated type requirements constrained generics, opaque result types, or manual type erasure is sufficient to support most use cases. To learn more, see the Protocols, Generics, and Opaque Types sections of The Swift Programming Language.


This educational note:

  • Explains the error message in unabbreviated English since it doesn't need to fit on a single line
  • Defines relevant terminology
  • Uses common standard library types to illustrate abstract language concepts
  • Explains why the limitation exists and suggests alternatives
  • Links to relevant chapters of The Swift Programming Language

Implementation Status

Complete:

  • Compiler infrastructure to easily add new educational notes
  • Educational notes can be included as part of printed diagnostic output
  • Educational notes are exposed through SourceKit
  • Half a dozen notes have been written so far

Todo:

Open Questions

  • When should this feature be enabled? IMO it should be unconditionally enabled in SourceKit (and eventually .dia) output, leaving the decision of whether or not to present the information up to IDEs. However, it's less clear whether or not it should be on-by-default in printed output. We should try to find a good balance between discoverability and concise output here.

New Printed Diagnostics Style (enable using -Xfrontend -enable-experimental-diagnostic-formatting in a recent master snapshot)

This new printing style is inspired by Rust diagnostics, and has a few advantages over LLVM-style diagnostics:

Open questions

  • Is there any existing tooling which relies on parsing LLVM-style diagnostics which would be broken by this change(e.g. emacs jump to error)?
  • What flags should be used to control the diagnostics style? IMO, if color is disabled the LLVM style diagnostics should be used, and the new style should be the default if color is supported. We could also add a new flag to opt into LLVM style color diagnostics if there are use cases which require it.
  • How can we handle multi-byte and variable width unicode characters better in errors? Right now we fall back to simpler display methods to ensure correctness.

Implementation Status

Complete:

  • Everything in the screenshots above :)

Todo:

28 Likes

A brief update:

I'm happy to announce that most of the educational notes infrastructure has now landed on master, and the feature is now enabled by default! As a result, the hidden frontend flag enable-educational-notes has been removed. Notes can be shown on the command line by passing the new -print-educational-notes flag.

I've also created a jira task to track expanding coverage to more diagnostics, and added a quick-start guide which assumes no prior knowledge or contributions if anyone is interested in adding their own.

11 Likes

Is there any update on this? Especially about support of this diagnostic messages in Xcode? (Or maybe they are working already but I managed to failed to enable it)

1 Like