From b9bf6adfedd4f4a6282474029f03804694ebce35 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 6 Jan 2023 11:01:43 +0100 Subject: [PATCH 01/10] The `#[diagnostic]` attribute namespace --- text/3366-diagnostic-attribute-namespace.md | 222 ++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 text/3366-diagnostic-attribute-namespace.md diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md new file mode 100644 index 00000000000..dee9d53aa72 --- /dev/null +++ b/text/3366-diagnostic-attribute-namespace.md @@ -0,0 +1,222 @@ +- Feature Name: The `#[diagnostic]` attribute namespace +- Start Date: 2023-01-06 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + + +# Summary +[summary]: #summary + +This RFC proposed to add a stable `#[diagnostic]` attribute namespace, which contains attributes to influence error messages emitted by the compiler. In addition it proposed to add a `#[diagnostic::on_unimplemented]` attribute to influence error messages emitted by unsatisfied traits bounds. + +# Motivation +[motivation]: #motivation + +Rust has the reputation to generate helpful error messages when something goes wrong. Nevertheless there are always cases of error messages that can be improved. One common example of such error messages in the rust ecosystem are those that are generated by crates using the type system to verify certain invariants at compile time. While these crates provide additional guarantees about these invariants, they sometimes generate large incomprehensible error messages when something goes wrong. These error messages do not always indicate clearly what went wrong. Well known examples of crates with such issues include bevy, axum or diesel. In some situations such a specific error message always indicates a certain problem. By providing authors of such crates tools to control the error messages emitted by the compiler they can improve the situation on their own. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +This feature has two possible groups of users: + +* Users that develop code and consume error messages from the compiler +* Users that write crates involving complex type hierarchies + +The first user group will interact with the proposed feature through the error messages emitted by the compiler. As of this I do not expect any major documentation requirements for this group of users. Although we might want to indicate that a certain error message was provided via the described feature set, rather than by the compiler itself to prevent users for filling issues about bad error messages in the compilers issue tracker. + +The second user group interacts with the described feature through attributes. These attributes allow them to hint the compiler to emit specific error messages in certain cases. The `#[diagnostic]` attribute namespace provides a general framework for what can and can't be done by such an attribute. As of this users won't interact directly with the attribute namespace itself. + +The `#[diagnostic::on_unimplemented]` attribute allows to hint the compiler to emit a specific error message if a certain trait is not implemented. This attribute should provide the following interface: + +```rust +#[diagnostic::on_unimplemented( + message="message", + label="label", + note="note" +)] +trait MyIterator { + fn next(&mut self) -> A; +} + + +fn iterate_chars>(i: I) { + // ... +} +fn main() { + iterate_chars(&[1, 2, 3][..]); +} +``` + +which might result in the compiler emitting the following error message: + +``` +error[E0277]: message + --> :14:5 + | +14 | iterate_chars(&[1, 2, 3][..]); + | ^^^^^^^^^^^^^ label + | + = note: note + = help: the trait `MyIterator` is not implemented for `&[{integer}]` + = note: required by `iterate_chars` +``` + +I expect the new attributes to be documented on the existing [Diagnostics](https://doc.rust-lang.org/reference/attributes/diagnostics.html) attributes page on the rust reference similar to existing attributes like for example `#[deprecated]` + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## The `#[diagnostic]` attribute namespace + +This RFC proposes to introduce a new `#[diagnostic]` attribute namespace. This namespace is supposed to contain different attributes, which allow users to hint the compiler to emit specific error messages in certain cases like type mismatches, unsatisfied trait bounds or similar situations. By collecting such attributes in a common namespace it is easier for users to find useful attributes and it is easier for the language team to establish a set of common rules for these attributes. + +Any attribute in this namespace may: + +* Hint the compiler to emit a specific error message in a specific situation + +Any attribute in this namespace is not allowed to: + +* Change the result of the compilation, which means applying such an attribute should never cause an error as long as they are syntactically valid + +The compiler is allowed to: + +* Ignore the provided hints +* Use only parts of the provided hints, for example for the proposed `#[diagnostic::on_unimplemented]` only use the `message` option +* Change this behaviour at any time + +The compiler must: + +* Verify that the attribute is syntactically correct, which means: + + Its one of the accepted attributes + + It only contains the allowed options + + Any provided option follows the defined syntax +* Follow the described semantics if the attribute is not ignored. + + +## The `#[diagnostic::on_unimplemented]` attribute + +This section describes the syntax of the `on_unimplemented` attribute and additionally how it is supposed to work. Implementing the later part is optional as outlined in the previous section. Any syntax not explicitly described in this section must be rejected as invalid according to the general rules of the `#[diagnostic]` attribute namespace. + +```rust +#[diagnostic::on_unimplemented( + message="message", + label="label", + note="note", +)] +trait MyIterator { + fn next(&mut self) -> A; +} +``` + + +Each of the options `message`, `label` and `note` are optional. They are separated by comma. The tailing comma is optional. Specifying any of these options hints the compiler to replace the normally emitted part of the error message with the provided string. At least one of these options needs to exist. Each option can appear at most once. The error message can include type information for the `Self` type or any generic type by using `{Self}` or `{A}` (where `A` refers to the generic type name in the definition). These placeholders are replaced by the actual type name. + +In addition the `on_unimplemented` attribute provides mechanisms to specify for which exact types a certain message should be emitted via an `if()` option. It accepts a set of filter options. A filter option consists on the generic parameter name from the trait definition and a type path against which the parameter should be checked. These type path could either be a fully qualified path or refer to any type in the current scope. As a special generic parameter name `Self` is added to refer to the `Self` type of the trait implementation. A filter option evaluates to `true` if the corresponding generic parameter in the trait definition matches the specified type. The provided `message`/`note`/`label` options are only emitted if the filter operation evaluates to `true`. + +The `any` and `all` option allows to combine multiple filter options. The `any` option matches if one of the supplied filter options evaluates to `true`, the `all` option requires that all supplied filter options evaluate to true. `not` allows to negate a given filter option. It evaluates to `true` if the inner filter option evaluates to `false`. These options can be nested to construct complex filters. + +The `on_unimplemented` attribute can be applied multiple times to the same trait definition. Multiple attributes are evaluated in order. The first matching instance for each of the `message`/`label`/`note` options is emitted. +```rust +#[diagnostic::on_unimplemented( + if(Self = std::string::String), + note = "That's only emitted if Self == std::string::String" +)] +#[diagnostic::on_unimplemented( + if(A = String), // Refers to whatever `String` is in the current scope + note = "That's only emitted if A == String", +)] +#[diagnostic::on_unimplemented( + if(any(A = i32, Self = i32)), + note = "That's emitted if A or Self is a i32", +)] +// this attribute will not have any affect as +// the attribute above will always match before +#[diagnostic::on_unimplemented( + if(all(A = i32, Self = i32)), + note = "That's emitted if A and Self is a i32" +)] +#[diagnostic::on_unimplemented( + if(not(A = String)), + // and implicitly `A` is not a `i32` as that case is + // matched earlier + note = "That's emitted if A is not a `String`" +)] +#[diagnostic::on_unimplemented( + message="message", + label="label", + note="That's emitted if neither of the condition above are meet", +)] +trait MyIterator { + fn next(&mut self) -> A; +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +A possible drawback is that this feature adds additional complexity to the compiler implementation. The compiler needs to handle an additional attribute namespace with at least one additional attribute. + +Another drawback is that crates might hint lower quality error messages than the compiler itself. Technically the compiler would be free to ignore such hints, practically I would assume that it is impossible to judge the quality of such error messages in an automated way. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +This proposal tries to improve error messages generated by rustc. It would give crate authors a tool to influence what error message is emitted in a certain situation, as they might sometimes want to provide specific details on certain error conditions. Not implementing this proposal would result in the current status quo. Currently the compiler always shows a "general" error message, even if it would be helpful to show additional details. + +There are alternative designs for the proposed `on_unimplemented` attribute: + +* The `on()` based filtering might be replaceable by placing the attribute on negative trait impls. This would turn a filter like +```rust +#[diagnostic::on_unimplemented( + on(Self = `String`, message = "Strings do not implement `IntoIterator` directly") +)] +trait IntoIterator {} +``` + +into the following negative trait impl: +```rust +#[diagnostic::on_unimplemented(message = "Strings do not implement `IntoIterator` directly")] +impl !IntoIterator for String {} +``` + +This would simplify the syntax of the proposed attribute, but in turn block the implementation of type based filtering on the stabilization of `negative_impls`. On the other hand it would likely simplify writing more complex filters, that match only a certain generic set of types and it would prevent "duplicating" the filter-logic as this reuses the exiting trait system. The large disadvantage of this approach is that it couples error messages to the crates public API. Removing a negative trait impl is a breaking change, removing a `#[on_unimplemented]` attribute is only a change in the emitted compiler error. + +* Allow `#[diagnostic::on_unimplemented]` to be placed on types instead of traits. This would allow third party crates to customize the error messages emitted for unsatisfied trait bounds with out of crate traits. This feels more like an extension tho the proposed attribute. + +# Prior art +[prior-art]: #prior-art + +* [rustc_on_unimplemented](https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented) already provides the described functionality as rustc internal attribute. It is used for improving error messages for various standard library API's. [This repo](https://github.com/weiznich/rust-foundation-community-grant/) contains several examples on how this attribute can be used in external crates to improve their error messages. +* [GHC](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/type_errors.html) provides an Haskell language extension for specifying custom compile time errors + +Notably all of the listed similar features are unofficial language extensions. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +Clarify the procedure of various potential changes prior stabilisation of the attribute namespace: + +* Process of adding a new attribute to the `#[diagnostics]` attribute macro namespace + + Requires a RFC? (t-lang seems to prefer that?) + + Requires a language/compiler MCP? + + Just a PR to the compiler? +* Process of extending an existing attribute by adding more options + + Requires a RFC? + + Requires a language/compiler MCP? + + Just a PR to the compiler? +* Process of removing support for specific options or an entire attribute from rustc + + Requires a compiler MCP? + + Just a PR to the compiler? +* Exact syntax of the `on_unimplemented` attribute + + Can `Self` be accepted in that position or do we need another name? + + Is `if()` a valid identifier in the proposed position? + +# Future possibilities +[future-possibilities]: #future-possibilities + +* More attributes like `#[diagnostics::on_type_error]` +* Extend the `#[diagnostics::on_unimplemented]` attribute to incorporate the semantics of `#[do_not_recommend]` +* Un-RFC `#[do_not_recommend]`? +* Apply `#[diagnostics::on_unimplemented]` to types as well +* Extend the `if()` filter syntax to allow more complex filter expressions From 74958a3c76a3c5174f391dd2d354c2f3586aedc7 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 10 Jan 2023 18:48:54 +0000 Subject: [PATCH 02/10] Apply suggestions from code review Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- text/3366-diagnostic-attribute-namespace.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index dee9d53aa72..1ef0073239f 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -110,11 +110,11 @@ trait MyIterator { ``` -Each of the options `message`, `label` and `note` are optional. They are separated by comma. The tailing comma is optional. Specifying any of these options hints the compiler to replace the normally emitted part of the error message with the provided string. At least one of these options needs to exist. Each option can appear at most once. The error message can include type information for the `Self` type or any generic type by using `{Self}` or `{A}` (where `A` refers to the generic type name in the definition). These placeholders are replaced by the actual type name. +Each of the options `message`, `label` and `note` are optional. They are separated by comma. The trailing comma is optional. Specifying any of these options hints the compiler to replace the normally emitted part of the error message with the provided string. At least one of these options needs to exist. Each option can appear at most once. The error message can include type information for the `Self` type or any generic type by using `{Self}` or `{A}` (where `A` refers to the generic type name in the definition). These placeholders are replaced by the actual type name. -In addition the `on_unimplemented` attribute provides mechanisms to specify for which exact types a certain message should be emitted via an `if()` option. It accepts a set of filter options. A filter option consists on the generic parameter name from the trait definition and a type path against which the parameter should be checked. These type path could either be a fully qualified path or refer to any type in the current scope. As a special generic parameter name `Self` is added to refer to the `Self` type of the trait implementation. A filter option evaluates to `true` if the corresponding generic parameter in the trait definition matches the specified type. The provided `message`/`note`/`label` options are only emitted if the filter operation evaluates to `true`. +In addition the `on_unimplemented` attribute provides mechanisms to specify for which exact types a certain message should be emitted via an `if()` option. It accepts a set of filter options. A filter option consists of the generic parameter name from the trait definition and a type path against which the parameter should be checked. This type path could either be a fully qualified path or refer to any type in the current scope. As a special generic parameter name `Self` is added to refer to the `Self` type of the trait implementation. A filter option evaluates to `true` if the corresponding generic parameter in the trait definition matches the specified type. The provided `message`/`note`/`label` options are only emitted if the filter operation evaluates to `true`. -The `any` and `all` option allows to combine multiple filter options. The `any` option matches if one of the supplied filter options evaluates to `true`, the `all` option requires that all supplied filter options evaluate to true. `not` allows to negate a given filter option. It evaluates to `true` if the inner filter option evaluates to `false`. These options can be nested to construct complex filters. +The `any` and `all` options allow to combine multiple filter options. The `any` option matches if one of the supplied filter options evaluates to `true`, the `all` option requires that all supplied filter options evaluate to true. `not` allows to negate a given filter option. It evaluates to `true` if the inner filter option evaluates to `false`. These options can be nested to construct complex filters. The `on_unimplemented` attribute can be applied multiple times to the same trait definition. Multiple attributes are evaluated in order. The first matching instance for each of the `message`/`label`/`note` options is emitted. ```rust @@ -182,13 +182,13 @@ impl !IntoIterator for String {} This would simplify the syntax of the proposed attribute, but in turn block the implementation of type based filtering on the stabilization of `negative_impls`. On the other hand it would likely simplify writing more complex filters, that match only a certain generic set of types and it would prevent "duplicating" the filter-logic as this reuses the exiting trait system. The large disadvantage of this approach is that it couples error messages to the crates public API. Removing a negative trait impl is a breaking change, removing a `#[on_unimplemented]` attribute is only a change in the emitted compiler error. -* Allow `#[diagnostic::on_unimplemented]` to be placed on types instead of traits. This would allow third party crates to customize the error messages emitted for unsatisfied trait bounds with out of crate traits. This feels more like an extension tho the proposed attribute. +* Allow `#[diagnostic::on_unimplemented]` to be placed on types instead of traits. This would allow third party crates to customize the error messages emitted for unsatisfied trait bounds with out of crate traits. This feels more like an extension to the proposed attribute. # Prior art [prior-art]: #prior-art * [rustc_on_unimplemented](https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented) already provides the described functionality as rustc internal attribute. It is used for improving error messages for various standard library API's. [This repo](https://github.com/weiznich/rust-foundation-community-grant/) contains several examples on how this attribute can be used in external crates to improve their error messages. -* [GHC](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/type_errors.html) provides an Haskell language extension for specifying custom compile time errors +* [GHC](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/type_errors.html) provides a Haskell mechanism for specifying custom compile time errors Notably all of the listed similar features are unofficial language extensions. From 30f575cb038eb0cacfaa2508ffb6bfb4ebe6b338 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 10 Jan 2023 19:51:08 +0100 Subject: [PATCH 03/10] Incorporate suggestions made during review --- text/3366-diagnostic-attribute-namespace.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index 1ef0073239f..fe5ffaf060a 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -180,9 +180,8 @@ into the following negative trait impl: impl !IntoIterator for String {} ``` -This would simplify the syntax of the proposed attribute, but in turn block the implementation of type based filtering on the stabilization of `negative_impls`. On the other hand it would likely simplify writing more complex filters, that match only a certain generic set of types and it would prevent "duplicating" the filter-logic as this reuses the exiting trait system. The large disadvantage of this approach is that it couples error messages to the crates public API. Removing a negative trait impl is a breaking change, removing a `#[on_unimplemented]` attribute is only a change in the emitted compiler error. +This would simplify the syntax of the proposed attribute, but in turn block the implementation of type based filtering on the stabilization of `negative_impls`. On the other hand it would likely simplify writing more complex filters, that match only a certain generic set of types and it would prevent "duplicating" the filter-logic as this reuses the exiting trait system. To express complex filtering logic this would likely need some sort of `specialization` for at least negative trait implementations. A second disadvantage of this approach is that it couples error messages to the crates public API. Removing a negative trait impl is a breaking change, removing a `#[on_unimplemented]` attribute is only a change in the emitted compiler error. -* Allow `#[diagnostic::on_unimplemented]` to be placed on types instead of traits. This would allow third party crates to customize the error messages emitted for unsatisfied trait bounds with out of crate traits. This feels more like an extension to the proposed attribute. # Prior art [prior-art]: #prior-art @@ -216,7 +215,9 @@ Clarify the procedure of various potential changes prior stabilisation of the at [future-possibilities]: #future-possibilities * More attributes like `#[diagnostics::on_type_error]` -* Extend the `#[diagnostics::on_unimplemented]` attribute to incorporate the semantics of `#[do_not_recommend]` +* Extend the `#[diagnostics::on_unimplemented]` attribute to incorporate the semantics of `#[do_not_recommend]` or +provide a distinct `#[diagnostics::do_not_recommend]` attribute * Un-RFC `#[do_not_recommend]`? * Apply `#[diagnostics::on_unimplemented]` to types as well * Extend the `if()` filter syntax to allow more complex filter expressions +* Allow `#[diagnostic::on_unimplemented]` to be placed on types instead of traits. This would allow third party crates to customize the error messages emitted for unsatisfied trait bounds with out of crate traits. From a9fc409563a972db9297e86d2ef803dd194c8829 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 27 Jan 2023 10:51:36 +0100 Subject: [PATCH 04/10] Incorporate suggestions from the @joshtriplett around the general semantics of the `#[diagnostic]` attribute namespace --- text/3366-diagnostic-attribute-namespace.md | 53 +++++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index fe5ffaf060a..a2f381652d7 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -71,32 +71,50 @@ I expect the new attributes to be documented on the existing [Diagnostics](https This RFC proposes to introduce a new `#[diagnostic]` attribute namespace. This namespace is supposed to contain different attributes, which allow users to hint the compiler to emit specific error messages in certain cases like type mismatches, unsatisfied trait bounds or similar situations. By collecting such attributes in a common namespace it is easier for users to find useful attributes and it is easier for the language team to establish a set of common rules for these attributes. +Attributes in this namespace are generally expected to be formed like: +```rust +#[diagnostic::attribute(option)] +``` +where several `option` entries can appear in the same attribute. `option` is expected to be a valid attribute argument in this position. Attributes in this namespace are versioned. Any incompatible change to an existing stable attribute in this namespace requires bumping the version of the diagnostic attribute namespace. Crates using these attributes must specify the expected version of the diagnostic namespace via a `#![diagnostic::v{version_number}]` crate top-level attribute. If multiple such attributes are present the compiler will choose the highest version number and ignore the rest. The compiler is encouraged to provide a lint for outdated versions of the diagnostic attribute namespace. This mechanism exists so that a third party crate can provide a shim implementation of the corresponding attributes to translate between different versions of the diagnostic attribute namespace on different rust versions. + Any attribute in this namespace may: * Hint the compiler to emit a specific error message in a specific situation +* Only affect the messages emitted by a compiler in case of a failed compilation Any attribute in this namespace is not allowed to: * Change the result of the compilation, which means applying such an attribute should never cause an error as long as they are syntactically valid +* pass-through information from the source of the diagnostic in a way that users can rely on. E.g. Such an attribute should not allow users to keep the compilation successful and dump information about `extern` blocks to generate C header files The compiler is allowed to: -* Ignore the provided hints -* Use only parts of the provided hints, for example for the proposed `#[diagnostic::on_unimplemented]` only use the `message` option -* Change this behaviour at any time +* Ignore the hints provided by: + + A specific attribute + + A specific option +* Change the set of supported attributes and options at any time +* Provide warning lints for malformed or otherwise not accepted attributes -The compiler must: +The compiler must not: -* Verify that the attribute is syntactically correct, which means: - + Its one of the accepted attributes - + It only contains the allowed options - + Any provided option follows the defined syntax -* Follow the described semantics if the attribute is not ignored. +* Change the semantic of an attribute or option, these kind of change requires a bump of the diagnostic attribute space version number +* Emit an hard error on malformed attribute, although emitting lints is fine +Adding a new attribute or option to the `#[diagnostic]` namespace is a decision of the compiler team. The envisioned procedure for this is as following: + +1. The new attribute lands on nightly without a feature gate. It gets ignored on stable/beta until its explicitly activated there. +2. The time as nightly only attribute is used to verify that: + * The attribute solves a real problem + * The implemented syntax matches the expectations +3. To enable a certain attribute in stable release the following steps are required: + * The attribute is documented in the rust reference + * T-lang is informed + * T-compiler performs a FCP, where a simple majority (>50%) accepts the attribute + ## The `#[diagnostic::on_unimplemented]` attribute -This section describes the syntax of the `on_unimplemented` attribute and additionally how it is supposed to work. Implementing the later part is optional as outlined in the previous section. Any syntax not explicitly described in this section must be rejected as invalid according to the general rules of the `#[diagnostic]` attribute namespace. +This section describes the syntax of the `on_unimplemented` attribute and additionally how it is supposed to work. The specification of this attribute is partially provided as example and motivation for the `#[diagnostic]` attribute namespace. In addition it is provided to give this RFC a ```rust #[diagnostic::on_unimplemented( @@ -164,6 +182,10 @@ Another drawback is that crates might hint lower quality error messages than the This proposal tries to improve error messages generated by rustc. It would give crate authors a tool to influence what error message is emitted in a certain situation, as they might sometimes want to provide specific details on certain error conditions. Not implementing this proposal would result in the current status quo. Currently the compiler always shows a "general" error message, even if it would be helpful to show additional details. +There are alternatives for the naming of the `#[diagnostic]` namespace: + +* Use a `#[rustc]` namespace for these attributes. This would signifies that these are rustc specific extensions. We likely want to encourage other rust implementations to utilise these information as well, therefore a more general attribute namespace seems to be a better solution. + There are alternative designs for the proposed `on_unimplemented` attribute: * The `on()` based filtering might be replaceable by placing the attribute on negative trait impls. This would turn a filter like @@ -196,17 +218,6 @@ Notably all of the listed similar features are unofficial language extensions. Clarify the procedure of various potential changes prior stabilisation of the attribute namespace: -* Process of adding a new attribute to the `#[diagnostics]` attribute macro namespace - + Requires a RFC? (t-lang seems to prefer that?) - + Requires a language/compiler MCP? - + Just a PR to the compiler? -* Process of extending an existing attribute by adding more options - + Requires a RFC? - + Requires a language/compiler MCP? - + Just a PR to the compiler? -* Process of removing support for specific options or an entire attribute from rustc - + Requires a compiler MCP? - + Just a PR to the compiler? * Exact syntax of the `on_unimplemented` attribute + Can `Self` be accepted in that position or do we need another name? + Is `if()` a valid identifier in the proposed position? From 6e9038bd992ea4dac461ff5a20399b37be9e874b Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 27 Jan 2023 10:56:36 +0100 Subject: [PATCH 05/10] Clarify the ordering semantics of the `on_unimplemented` attribute regarding options and compiler warnings. --- text/3366-diagnostic-attribute-namespace.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index a2f381652d7..58c8f2e2536 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -134,7 +134,7 @@ In addition the `on_unimplemented` attribute provides mechanisms to specify for The `any` and `all` options allow to combine multiple filter options. The `any` option matches if one of the supplied filter options evaluates to `true`, the `all` option requires that all supplied filter options evaluate to true. `not` allows to negate a given filter option. It evaluates to `true` if the inner filter option evaluates to `false`. These options can be nested to construct complex filters. -The `on_unimplemented` attribute can be applied multiple times to the same trait definition. Multiple attributes are evaluated in order. The first matching instance for each of the `message`/`label`/`note` options is emitted. +The `on_unimplemented` attribute can be applied multiple times to the same trait definition. Multiple attributes are evaluated in order. The first matching instance for each of the `message`/`label`/`note` options is emitted. The compiler may lint against ignored variants as this is the case for `match` arms for example. ```rust #[diagnostic::on_unimplemented( if(Self = std::string::String), From cecccb40a375694f30b51797b6a71a282ed22982 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 27 Jan 2023 17:12:39 +0100 Subject: [PATCH 06/10] Finish a cutoff sentence --- text/3366-diagnostic-attribute-namespace.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index 58c8f2e2536..eb42c750c92 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -114,7 +114,7 @@ Adding a new attribute or option to the `#[diagnostic]` namespace is a decision ## The `#[diagnostic::on_unimplemented]` attribute -This section describes the syntax of the `on_unimplemented` attribute and additionally how it is supposed to work. The specification of this attribute is partially provided as example and motivation for the `#[diagnostic]` attribute namespace. In addition it is provided to give this RFC a +This section describes the syntax of the `on_unimplemented` attribute and additionally how it is supposed to work. The specification of this attribute is partially provided as example and motivation for the `#[diagnostic]` attribute namespace. In addition it is provided to give this RFC a concrete use, such that we not only define an empty attribute namespace. ```rust #[diagnostic::on_unimplemented( From c6b8f65222fd83c83f3cf019654c97e7fd4e1f0f Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 28 Apr 2023 09:29:26 +0200 Subject: [PATCH 07/10] Incorporate feedback from the design meeting --- text/3366-diagnostic-attribute-namespace.md | 42 ++++++++++----------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index eb42c750c92..bd1b0a9f347 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -69,48 +69,41 @@ I expect the new attributes to be documented on the existing [Diagnostics](https ## The `#[diagnostic]` attribute namespace -This RFC proposes to introduce a new `#[diagnostic]` attribute namespace. This namespace is supposed to contain different attributes, which allow users to hint the compiler to emit specific error messages in certain cases like type mismatches, unsatisfied trait bounds or similar situations. By collecting such attributes in a common namespace it is easier for users to find useful attributes and it is easier for the language team to establish a set of common rules for these attributes. +This RFC proposes to introduce a new `#[diagnostic]` attribute namespace. This namespace is supposed to contain different attributes, which allow users to hint the compiler to emit specific diagnostic messages in certain cases like type mismatches, unsatisfied trait bounds or similar situations. By collecting such attributes in a common namespace it is easier for users to find useful attributes and it is easier for the language team to establish a set of common rules for these attributes. Attributes in this namespace are generally expected to be formed like: ```rust #[diagnostic::attribute(option)] ``` -where several `option` entries can appear in the same attribute. `option` is expected to be a valid attribute argument in this position. Attributes in this namespace are versioned. Any incompatible change to an existing stable attribute in this namespace requires bumping the version of the diagnostic attribute namespace. Crates using these attributes must specify the expected version of the diagnostic namespace via a `#![diagnostic::v{version_number}]` crate top-level attribute. If multiple such attributes are present the compiler will choose the highest version number and ignore the rest. The compiler is encouraged to provide a lint for outdated versions of the diagnostic attribute namespace. This mechanism exists so that a third party crate can provide a shim implementation of the corresponding attributes to translate between different versions of the diagnostic attribute namespace on different rust versions. +where several `option` entries can appear in the same attribute. `option` is expected to be a valid attribute argument in this position. Any attribute in this namespace may: -* Hint the compiler to emit a specific error message in a specific situation -* Only affect the messages emitted by a compiler in case of a failed compilation +* Hint the compiler to emit a specific diagnostic message in a specific situation +* Only affect the messages emitted by a compiler Any attribute in this namespace is not allowed to: -* Change the result of the compilation, which means applying such an attribute should never cause an error as long as they are syntactically valid -* pass-through information from the source of the diagnostic in a way that users can rely on. E.g. Such an attribute should not allow users to keep the compilation successful and dump information about `extern` blocks to generate C header files +* Change the result of the compilation, which means applying such an attribute should never cause a compilation error as long as they are syntactically valid +* Pass-through information from the source of the diagnostic in a way that users can rely on. E.g. Such an attribute should not allow users to keep the compilation successful and dump information about `extern` blocks to generate C header files The compiler is allowed to: * Ignore the hints provided by: + A specific attribute + A specific option -* Change the set of supported attributes and options at any time -* Provide warning lints for malformed or otherwise not accepted attributes +* Change the support for a specific attribute or option at any time The compiler must not: -* Change the semantic of an attribute or option, these kind of change requires a bump of the diagnostic attribute space version number -* Emit an hard error on malformed attribute, although emitting lints is fine +* Change the semantic of an attribute or option +* Emit an hard error on malformed attribute -Adding a new attribute or option to the `#[diagnostic]` namespace is a decision of the compiler team. The envisioned procedure for this is as following: +The compiler should: -1. The new attribute lands on nightly without a feature gate. It gets ignored on stable/beta until its explicitly activated there. -2. The time as nightly only attribute is used to verify that: - * The attribute solves a real problem - * The implemented syntax matches the expectations -3. To enable a certain attribute in stable release the following steps are required: - * The attribute is documented in the rust reference - * T-lang is informed - * T-compiler performs a FCP, where a simple majority (>50%) accepts the attribute - +* Emit implement a warn-by-default lint for unrecognised attributes or options + +Adding a new attribute or option to the `#[diagnostic]` namespace is for now a decision of the language team. The language team can delegate these decisions partially or completely to a different team without requiring a new RFC. ## The `#[diagnostic::on_unimplemented]` attribute @@ -128,13 +121,13 @@ trait MyIterator { ``` -Each of the options `message`, `label` and `note` are optional. They are separated by comma. The trailing comma is optional. Specifying any of these options hints the compiler to replace the normally emitted part of the error message with the provided string. At least one of these options needs to exist. Each option can appear at most once. The error message can include type information for the `Self` type or any generic type by using `{Self}` or `{A}` (where `A` refers to the generic type name in the definition). These placeholders are replaced by the actual type name. +Each of the options `message`, `label` and `note` are optional. They are separated by comma. The trailing comma is optional. Specifying any of these options hints the compiler to replace the normally emitted part of the error message with the provided string. At least one of these options needs to exist. Each option can appear at most once. These options can include type information for the `Self` type or any generic type by using `{Self}` or `{A}` (where `A` refers to the generic type name in the definition). These placeholders are replaced by the compiler with the actual type name. In addition the `on_unimplemented` attribute provides mechanisms to specify for which exact types a certain message should be emitted via an `if()` option. It accepts a set of filter options. A filter option consists of the generic parameter name from the trait definition and a type path against which the parameter should be checked. This type path could either be a fully qualified path or refer to any type in the current scope. As a special generic parameter name `Self` is added to refer to the `Self` type of the trait implementation. A filter option evaluates to `true` if the corresponding generic parameter in the trait definition matches the specified type. The provided `message`/`note`/`label` options are only emitted if the filter operation evaluates to `true`. The `any` and `all` options allow to combine multiple filter options. The `any` option matches if one of the supplied filter options evaluates to `true`, the `all` option requires that all supplied filter options evaluate to true. `not` allows to negate a given filter option. It evaluates to `true` if the inner filter option evaluates to `false`. These options can be nested to construct complex filters. -The `on_unimplemented` attribute can be applied multiple times to the same trait definition. Multiple attributes are evaluated in order. The first matching instance for each of the `message`/`label`/`note` options is emitted. The compiler may lint against ignored variants as this is the case for `match` arms for example. +The `on_unimplemented` attribute can be applied multiple times to the same trait definition. Multiple attributes are evaluated in order. The first matching instance for each of the `message`/`label`/`note` options is emitted. The compiler should provide a warn-by-default lint for ignored variants as this is the case for `match` arms. ```rust #[diagnostic::on_unimplemented( if(Self = std::string::String), @@ -225,6 +218,11 @@ Clarify the procedure of various potential changes prior stabilisation of the at # Future possibilities [future-possibilities]: #future-possibilities +* Add a versioning scheme + + For specific attributes + + For the namespace + + Based on editions + + Custom versioning scheme * More attributes like `#[diagnostics::on_type_error]` * Extend the `#[diagnostics::on_unimplemented]` attribute to incorporate the semantics of `#[do_not_recommend]` or provide a distinct `#[diagnostics::do_not_recommend]` attribute From cb8f2fc5aec3b4f2d35a6f017fd7e9e59eb91fa7 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 9 May 2023 19:56:34 +0200 Subject: [PATCH 08/10] Replace another use of `on()` with the newer `if()` syntax --- text/3366-diagnostic-attribute-namespace.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index bd1b0a9f347..bafe1bf5f7c 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -181,7 +181,7 @@ There are alternatives for the naming of the `#[diagnostic]` namespace: There are alternative designs for the proposed `on_unimplemented` attribute: -* The `on()` based filtering might be replaceable by placing the attribute on negative trait impls. This would turn a filter like +* The `if()` based filtering might be replaceable by placing the attribute on negative trait impls. This would turn a filter like ```rust #[diagnostic::on_unimplemented( on(Self = `String`, message = "Strings do not implement `IntoIterator` directly") From 4b112aa130e43c55fbd3d772d39ce54a594e2638 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 9 May 2023 20:00:19 +0200 Subject: [PATCH 09/10] Readd some details around the different version schemes --- text/3366-diagnostic-attribute-namespace.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index bafe1bf5f7c..78bde4bfb68 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -219,10 +219,11 @@ Clarify the procedure of various potential changes prior stabilisation of the at [future-possibilities]: #future-possibilities * Add a versioning scheme - + For specific attributes - + For the namespace + + For specific attributes via `#[diagnostic::attribute(version = 42)]` + + For the namespace via a crate level `#[diagnostic::v{version_number}]` attribute + Based on editions + Custom versioning scheme + + (Each of these variants can be added in a backward compatible way if needed) * More attributes like `#[diagnostics::on_type_error]` * Extend the `#[diagnostics::on_unimplemented]` attribute to incorporate the semantics of `#[do_not_recommend]` or provide a distinct `#[diagnostics::do_not_recommend]` attribute From 0d519ab6cb8cb9c071f6983488f96a0bd9f3a055 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 9 May 2023 20:08:15 +0200 Subject: [PATCH 10/10] Slightly change to wording around the definition of the `#[diagnostic]` namespace This hopefully addresses both concerns raised by petrochenkov: > Does #[diagnostic::something] behave like #[rustfmt::skip] from name resolution / macro expansion point of view? By specifying that `#[diagnostic]` is a built-in tool attribute > How many of these attributes will exist - 2, 3, 5? > Why is it necessary to add a mechanism for grouping them instead of just using built-in attributes? By being explicit about that this namespace also serves an organizational need by defining a common set of rules. This hopefully allows the language team to delegate the decisions about the design of specific attributes to other teams. --- text/3366-diagnostic-attribute-namespace.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3366-diagnostic-attribute-namespace.md b/text/3366-diagnostic-attribute-namespace.md index 78bde4bfb68..d60d55b124e 100644 --- a/text/3366-diagnostic-attribute-namespace.md +++ b/text/3366-diagnostic-attribute-namespace.md @@ -69,7 +69,7 @@ I expect the new attributes to be documented on the existing [Diagnostics](https ## The `#[diagnostic]` attribute namespace -This RFC proposes to introduce a new `#[diagnostic]` attribute namespace. This namespace is supposed to contain different attributes, which allow users to hint the compiler to emit specific diagnostic messages in certain cases like type mismatches, unsatisfied trait bounds or similar situations. By collecting such attributes in a common namespace it is easier for users to find useful attributes and it is easier for the language team to establish a set of common rules for these attributes. +This RFC proposes to introduce a new built-in `#[diagnostic]` tool attribute namespace. This namespace is supposed to contain different attributes, which allow users to hint the compiler to emit specific diagnostic messages in certain cases like type mismatches, unsatisfied trait bounds or similar situations. By collecting such attributes in a common namespace it is easier for users to find useful attributes and it is easier for the language team to establish a set of common rules for these attributes. This opens the possibility to delegate the design of specific attributes to other teams later on. Attributes in this namespace are generally expected to be formed like: ```rust