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:
- Add support to the diagnostic verifier so we can more easily write tests which ensure notes are emitted properly (In progress: [DiagnosticVerifier] Make DiagnosticVerifier a DiagnosticConsumer by owenv · Pull Request #28313 · apple/swift · GitHub, [Diagnostics] Add verifier support for educational notes by owenv · Pull Request #28964 · apple/swift · GitHub)
- Add basic terminal markdown rendering for nicer presentation in printed output (In progress: [Diagnostics] Add a basic terminal markdown printer for educational notes by owenv · Pull Request #28612 · apple/swift · GitHub)
- Implement support for including educational notes in the serialized diagnostics (.dia) format. This is consumed by Xcode, but changes to the format need to be coordinated with Clang.
- Expand note coverage to more diagnostics (contributions here are especially welcome!, swift/Diagnostics.md at main · apple/swift · GitHub has some info on how to get started and no C++ knowledge is required)
- Add support to SourceKit-LSP, pending some upcoming changes to the protocol spec.
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:
- Notes are grouped together with their parent error or warning in a single report
- Messages are displayed in the context of the surrounding code
- Color is used more effectively to convey insertions/deletions/highlights/etc.


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:
- Refactor the DiagnosticVerifier so tests can run with the new style on-by-default (In progress: [DiagnosticVerifier] Make DiagnosticVerifier a DiagnosticConsumer by owenv · Pull Request #28313 · apple/swift · GitHub)
- Add driver support for the new style, pending discussion on what the final flags should be
- Lots of additional testing, maybe through some kind of fuzzer