diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
commit | 9918693037dce8aa4bb6f08741b6812923486c18 (patch) | |
tree | 21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /src/doc/rustc-dev-guide | |
parent | Releasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff) | |
download | rustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip |
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/doc/rustc-dev-guide')
21 files changed, 728 insertions, 79 deletions
diff --git a/src/doc/rustc-dev-guide/.mailmap b/src/doc/rustc-dev-guide/.mailmap new file mode 100644 index 000000000..1a1f6ffb6 --- /dev/null +++ b/src/doc/rustc-dev-guide/.mailmap @@ -0,0 +1,5 @@ +Jynn Nelson <github@jyn.dev> <jyn514@gmail.com> +Jynn Nelson <github@jyn.dev> <joshua@yottadb.com> +Jynn Nelson <github@jyn.dev> <jyn.nelson@redjack.com> +Jynn Nelson <github@jyn.dev> <jnelson@cloudflare.com> +Jynn Nelson <github@jyn.dev> diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 4a7fd2b77..e484b6af6 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -56,6 +56,7 @@ - [RISC-V](notification-groups/risc-v.md) - [Windows](notification-groups/windows.md) - [Licenses](./licenses.md) +- [Editions](guides/editions.md) # High-level Compiler Architecture @@ -123,6 +124,7 @@ - [Lowering to logic](./traits/lowering-to-logic.md) - [Goals and clauses](./traits/goals-and-clauses.md) - [Canonical queries](./traits/canonical-queries.md) + - [Canonicalization](./traits/canonicalization.md) - [Next-gen trait solving](./solve/trait-solving.md) - [Invariants of the type system](./solve/invariants.md) - [The solver](./solve/the-solver.md) @@ -130,6 +132,7 @@ - [Coinduction](./solve/coinduction.md) - [Proof trees](./solve/proof-trees.md) - [Normalization](./solve/normalization.md) + - [`Unsize` and `CoerceUnsized` traits](./traits/unsize.md) - [Type checking](./type-checking.md) - [Method Lookup](./method-lookup.md) - [Variance](./variance.md) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping.md b/src/doc/rustc-dev-guide/src/building/bootstrapping.md index 936c75f36..1f66f08f3 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping.md @@ -29,7 +29,7 @@ Note that this documentation mostly covers user-facing information. See - Stage 2: the truly current compiler - Stage 3: the same-result test -Compiling `rustc` is done in stages. Here's a diagram, adapted from Joshua Nelson's +Compiling `rustc` is done in stages. Here's a diagram, adapted from Jynn Nelson's [talk on bootstrapping][rustconf22-talk] at RustConf 2022, with detailed explanations below. The `A`, `B`, `C`, and `D` show the ordering of the stages of bootstrapping. diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 1012e94fd..253a94956 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -293,7 +293,7 @@ For example, if your host platform is `x86_64-unknown-linux-gnu` and your cross-compilation target is `wasm32-wasi`, you can build with: ```bash -./x build --target x86_64-unknown-linux-gnu --target wasm32-wasi +./x build --target x86_64-unknown-linux-gnu,wasm32-wasi ``` Note that if you want the resulting compiler to be able to build crates that diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index d5ba78c77..9922ee7dd 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -17,15 +17,15 @@ Currently the compiler team chats in Zulip: with rustc development, or [`t-compiler/meetings`][zulip-meetings], where the team holds their weekly triage and steering meetings. -## Expert map +## Reviewers If you're interested in figuring out who can answer questions about a particular part of the compiler, or you'd just like to know who works on what, -check out our [experts directory][experts]. +check out [triagebot.toml's assign section][map]. It contains a listing of the various parts of the compiler and a list of people -who are experts on each one. +who are reviewers of each part. -[experts]: https://github.com/rust-lang/compiler-team/blob/master/content/experts/map.toml +[map]: https://github.com/rust-lang/rust/blob/master/triagebot.toml ## Rust compiler meeting diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 9f4245f28..f21bae40f 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -523,7 +523,7 @@ are: - Early lint pass: Works on [AST nodes] after [macro expansion] and name resolution, just before [HIR lowering]. These lints are for purely syntactical lints. - - Example: The [`unsued_parens`] lint checks for parenthesized-expressions + - Example: The [`unused_parens`] lint checks for parenthesized-expressions in situations where they are not needed, like an `if` condition. - Late lint pass: Works on [HIR nodes], towards the end of [analysis] (after @@ -558,7 +558,7 @@ compiler](#linting-early-in-the-compiler). [macro expansion]: macro-expansion.md [analysis]: part-4-intro.md [`keyword_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#keyword-idents -[`unsued_parens`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unused-parens +[`unused_parens`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unused-parens [`invalid_value`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#invalid-value [`arithmetic_overflow`]: https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#arithmetic-overflow [`unused_mut`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unused-mut @@ -572,7 +572,6 @@ generally based on how they are registered. - *Built-in* lints are defined inside the compiler source. - *Driver-registered* lints are registered when the compiler driver is created by an external driver. This is the mechanism used by Clippy, for example. -- *Plugin* lints are registered by the [deprecated plugin system]. - *Tool* lints are lints with a path prefix like `clippy::` or `rustdoc::`. - *Internal* lints are the `rustc::` scoped tool lints that only run on the rustc source tree itself and are defined in the compiler source like a @@ -581,7 +580,6 @@ generally based on how they are registered. More information about lint registration can be found in the [LintStore] chapter. -[deprecated plugin system]: https://doc.rust-lang.org/nightly/unstable-book/language-features/plugin.html [LintStore]: diagnostics/lintstore.md ### Declaring a lint @@ -691,6 +689,8 @@ declare_lint! { This makes the `ANONYMOUS_PARAMETERS` lint allow-by-default in the 2015 edition but warn-by-default in the 2018 edition. +See [Edition-specific lints](../guides/editions.md#edition-specific-lints) for more information. + ### Feature-gated lints Lints belonging to a feature should only be usable if the feature is enabled in the @@ -722,6 +722,7 @@ meaning that rustc exclusively exposes to users as "future incompatible". meaning in an upcoming *edition*. These are often called "edition lints" and can be typically seen in the various "edition compatibility" lint groups (e.g., `rust_2021_compatibility`) that are used to lint against code that will break if the user updates the crate's edition. +See [migration lints](guides/editions.md#migration-lints) for more details. A future-incompatible lint should be declared with the `@future_incompatible` additional "field": diff --git a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md index 603c9ed65..54dd841ad 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md @@ -4,9 +4,8 @@ This page documents some of the machinery around lint registration and how we run lints in the compiler. The [`LintStore`] is the central piece of infrastructure, around which -everything rotates. It's not available during the early parts of compilation -(i.e., before TyCtxt) in most code, as we need to fill it in with all of the -lints, which can only happen after plugin registration. +everything rotates. The `LintStore` is held as part of the [`Session`], and it +gets populated with the list of lints shortly after the `Session` is created. ## Lints vs. lint passes @@ -39,16 +38,15 @@ lints are emitted as part of other work (e.g., type checking, etc.). ### High-level overview -In [`rustc_interface::register_plugins`], +In [`rustc_interface::run_compiler`], the [`LintStore`] is created, and all lints are registered. -There are four 'sources' of lints: +There are three 'sources' of lints: * internal lints: lints only used by the rustc codebase * builtin lints: lints built into the compiler and not provided by some outside source -* plugin lints: lints created by plugins through the plugin system. * `rustc_interface::Config`[`register_lints`]: lints passed into the compiler during construction @@ -56,8 +54,7 @@ Lints are registered via the [`LintStore::register_lint`] function. This should happen just once for any lint, or an ICE will occur. Once the registration is complete, we "freeze" the lint store by placing it in -an `Lrc`. Later in the driver, it's passed into the `GlobalCtxt` constructor -where it lives in an immutable form from then on. +an `Lrc`. Lint passes are registered separately into one of the categories (pre-expansion, early, late, late module). Passes are registered as a closure @@ -68,8 +65,8 @@ they can keep track of state internally. #### Internal lints -These are lints used just by the compiler or plugins like `clippy`. They can be -found in `rustc_lint::internal`. +These are lints used just by the compiler or drivers like `clippy`. They can be +found in [`rustc_lint::internal`]. An example of such a lint is the check that lint passes are implemented using the `declare_lint_pass!` macro and not by hand. This is accomplished with the @@ -92,18 +89,6 @@ the [`rustc_lint::register_builtins`] function. Just like with internal lints, this happens inside of [`rustc_lint::new_lint_store`]. -#### Plugin lints - -This is one of the primary use cases remaining for plugins/drivers. Plugins are -given access to the mutable `LintStore` during registration (which happens -inside of [`rustc_interface::register_plugins`]) and they can call any -functions they need on the `LintStore`, just like rustc code. - -Plugins are intended to declare lints with the `plugin` field set to true -(e.g., by way of the [`declare_tool_lint!`] macro), but this is purely for -diagnostics and help text; otherwise plugin lints are mostly just as first -class as rustc builtin lints. - #### Driver lints These are the lints provided by drivers via the `rustc_interface::Config` @@ -127,7 +112,6 @@ approach, it is beneficial to do so for performance reasons. [`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html [`LintStore::register_lint`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_lints -[`rustc_interface::register_plugins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/passes/fn.register_plugins.html [`rustc_lint::register_builtins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html [`rustc_lint::register_internals`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_internals.html [`rustc_lint::new_lint_store`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.new_lint_store.html @@ -135,3 +119,6 @@ approach, it is beneficial to do so for performance reasons. [`declare_tool_lint!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/macro.declare_tool_lint.html [`register_lints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints [`&rustc_lint_defs::Lint`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/struct.Lint.html +[`Session`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html +[`rustc_interface::run_compiler`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html#reexport.run_compiler +[`rustc_lint::internal`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/internal/index.html diff --git a/src/doc/rustc-dev-guide/src/feature-gates.md b/src/doc/rustc-dev-guide/src/feature-gates.md index 788f93d66..24ce9bb71 100644 --- a/src/doc/rustc-dev-guide/src/feature-gates.md +++ b/src/doc/rustc-dev-guide/src/feature-gates.md @@ -25,7 +25,7 @@ To remove a feature gate, follow these steps: ```rust,ignore /// description of feature - (unstable, $feature_name, "$version", Some($tracking_issue_number), $edition) + (unstable, $feature_name, "$version", Some($tracking_issue_number)) ``` 2. Add a modified version of the feature gate declaration that you just @@ -33,7 +33,7 @@ To remove a feature gate, follow these steps: ```rust,ignore /// description of feature - (removed, $old_feature_name, "$version", Some($tracking_issue_number), $edition, + (removed, $old_feature_name, "$version", Some($tracking_issue_number), Some("$why_it_was_removed")) ``` @@ -50,7 +50,7 @@ to follow when [removing a feature gate][removing]): ```rust,ignore /// description of feature - (unstable, $old_feature_name, "$version", Some($tracking_issue_number), $edition) + (unstable, $old_feature_name, "$version", Some($tracking_issue_number)) ``` 2. Add a modified version of the old feature gate declaration that you just @@ -59,7 +59,7 @@ to follow when [removing a feature gate][removing]): ```rust,ignore /// description of feature /// Renamed to `$new_feature_name` - (removed, $old_feature_name, "$version", Some($tracking_issue_number), $edition, + (removed, $old_feature_name, "$version", Some($tracking_issue_number), Some("renamed to `$new_feature_name`")) ``` @@ -69,7 +69,7 @@ to follow when [removing a feature gate][removing]): ```rust,ignore /// description of feature - (unstable, $new_feature_name, "$version", Some($tracking_issue_number), $edition) + (unstable, $new_feature_name, "$version", Some($tracking_issue_number)) ``` diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index a12cb068c..ef54a3009 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -47,12 +47,11 @@ guide :) ### Experts -Not all `t-compiler` members are experts on all parts of `rustc`; it's a pretty -large project. To find out who has expertise on different parts of the -compiler, [consult this "experts map"][map]. - -It's not perfectly complete, though, so please also feel free to ask questions -even if you can't figure out who to ping. +Not all `t-compiler` members are experts on all parts of `rustc`; it's a +pretty large project. To find out who could have some expertise on +different parts of the compiler, [consult traigebot assign groups][map]. +The sections that start with `[assign*` in `triagebot.toml` file. +But also, feel free to ask questions even if you can't figure out who to ping. Another way to find experts for a given part of the compiler is to see who has made recent commits. For example, to find people who have recently worked on name resolution since the 1.68.2 release, @@ -60,7 +59,7 @@ you could run `git shortlog -n 1.68.2.. compiler/rustc_resolve/`. Ignore any com "Rollup merge" or commits by `@bors` (see [CI contribution procedures](./contributing.md#ci) for more information about these commits). -[map]: https://github.com/rust-lang/compiler-team/blob/master/content/experts/map.toml +[map]: https://github.com/rust-lang/rust/blob/master/triagebot.toml ### Etiquette @@ -165,7 +164,6 @@ incredibly helpful: [users]: https://users.rust-lang.org/ [so]: http://stackoverflow.com/questions/tagged/rust [community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library - [iceb]: ./notification-groups/cleanup-crew.md [wd]: ./contributing.md#writing-documentation [wg]: https://rust-lang.github.io/compiler-team/working-groups/ diff --git a/src/doc/rustc-dev-guide/src/guides/editions.md b/src/doc/rustc-dev-guide/src/guides/editions.md new file mode 100644 index 000000000..336e391df --- /dev/null +++ b/src/doc/rustc-dev-guide/src/guides/editions.md @@ -0,0 +1,336 @@ +# Editions + +<!-- toc --> + +This chapter gives an overview of how Edition support works in rustc. +This assumes that you are familiar with what Editions are (see the [Edition Guide]). + +[Edition Guide]: https://doc.rust-lang.org/edition-guide/ + +## Edition definition + +The `--edition` CLI flag specifies the edition to use for a crate. +This can be accessed from [`Session::edition`]. +There are convenience functions like [`Session::at_least_rust_2021`] for checking the crate's +edition, though you should be careful about whether you check the global session or the span, see +[Edition hygiene] below. + +As an alternative to the `at_least_rust_20xx` convenience methods, the [`Edition`] type also +supports comparisons for doing range checks, such as `span.edition() >= Edition::Edition2021`. + +[`Session::edition`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html#method.edition +[`Session::at_least_rust_2021`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html#method.at_least_rust_2021 +[`Edition`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/edition/enum.Edition.html + +### Adding a new edition + +Adding a new edition mainly involves adding a variant to the [`Edition`] enum and then fixing +everything that is broken. See [#94461](https://github.com/rust-lang/rust/pull/94461) for an +example. + +### Features and Edition stability + +The [`Edition`] enum defines whether or not an edition is stable. +If it is not stable, then the `-Zunstable-options` CLI option must be passed to enable it. + +When adding a new feature, there are two options you can choose for how to handle stability with a +future edition: + +- Just check the edition of the span like `span.at_least_rust_20xx()` (see [Edition hygiene]) or the + [`Session::edition`]. This will implicitly depend on the stability of the edition itself to + indicate that your feature is available. +- Place your new behavior behind a [feature gate]. + +It may be sufficient to only check the current edition for relatively simple changes. +However, for larger language changes, you should consider creating a feature gate. +There are several benefits to using a feature gate: + +- A feature gate makes it easier to work on and experiment with a new feature. +- It makes the intent clear when the `#![feature(…)]` attribute is used that your new feature is + being enabled. +- It makes testing of editions easier so that features that are not yet complete do not interfere + with testing of edition-specific features that are complete and ready. +- It decouples the feature from an edition, which makes it easier for the team to make a deliberate + decision of whether or not a feature should be added to the next edition when the feature is + ready. + +When a feature is complete and ready, the feature gate can be removed (and the code should just +check the span or `Session` edition to determine if it is enabled). + +There are a few different options for doing feature checks: + +- For highly experimental features, that may or may not be involved in an edition, they can + implement regular feature gates like `tcx.features().my_feature`, and ignore editions for the time + being. + +- For experimental features that *might* be involved in an edition, they should implement gates with + `tcx.features().my_feature && span.at_least_rust_20xx()`. + This requires the user to still specify `#![feature(my_feature)]`, to avoid disrupting testing of + other edition features which are ready and have been accepted within the edition. + +- For experimental features that have graduated to definitely be part of an edition, + they should implement gates with `tcx.features().my_feature || span.at_least_rust_20xx()`, + or just remove the feature check altogether and just check `span.at_least_rust_20xx()`. + +If you need to do the feature gating in multiple places, consider placing the check in a single +function so that there will only be a single place to update. For example: + +```rust,ignore +// An example from Edition 2021 disjoint closure captures. + +fn enable_precise_capture(tcx: TyCtxt<'_>, span: Span) -> bool { + tcx.features().capture_disjoint_fields || span.rust_2021() +} +``` + +See [Lints and stability](#lints-and-stability) below for more information about how lints handle +stability. + +[feature gate]: ../feature-gates.md + +## Edition parsing + +For the most part, the lexer is edition-agnostic. +Within [`StringReader`], tokens can be modified based on edition-specific behavior. +For example, C-String literals like `c"foo"` are split into multiple tokens in editions before 2021. +This is also where things like reserved prefixes are handled for the 2021 edition. + +Edition-specific parsing is relatively rare. One example is `async fn` which checks the span of the +token to determine if it is the 2015 edition, and emits an error in that case. +This can only be done if the syntax was already invalid. + +If you need to do edition checking in the parser, you will normally want to look at the edition of +the token, see [Edition hygiene]. +In some rare cases you may instead need to check the global edition from [`ParseSess::edition`]. + +Most edition-specific parsing behavior is handled with [migration lints] instead of in the parser. +This is appropriate when there is a *change* in syntax (as opposed to new syntax). +This allows the old syntax to continue to work on previous editions. +The lint then checks for the change in behavior. +On older editions, the lint pass should emit the migration lint to help with migrating to new +editions. +On newer editions, your code should emit a hard error with `emit_err` instead. +For example, the deprecated `start...end` pattern syntax emits the +[`ellipsis_inclusive_range_patterns`] lint on editions before 2021, and in 2021 is an hard error via +the `emit_err` method. + +[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html +[`ParseSess::edition`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html#structfield.edition +[`ellipsis_inclusive_range_patterns`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#ellipsis-inclusive-range-patterns + +### Keywords + +New keywords can be introduced across an edition boundary. +This is implemented by functions like [`Symbol::is_used_keyword_conditional`], which rely on the +ordering of how the keywords are defined. + +When new keywords are introduced, the [`keyword_idents`] lint should be updated so that automatic +migrations can transition code that might be using the keyword as an identifier (see +[`KeywordIdents`]). +An alternative to consider is to implement the keyword as a weak keyword if the position it is used +is sufficient to distinguish it. + +An additional option to consider is the `k#` prefix which was introduced in [RFC 3101]. +This allows the use of a keyword in editions *before* the edition where the keyword is introduced. +This is currently not implemented. + +[`Symbol::is_used_keyword_conditional`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html#method.is_used_keyword_conditional +[`keyword_idents`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/allowed-by-default.html#keyword-idents +[`KeywordIdents`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/builtin/struct.KeywordIdents.html +[RFC 3101]: https://rust-lang.github.io/rfcs/3101-reserved_prefixes.html + +### Edition hygiene +[edition hygiene]: #edition-hygiene + +Spans are marked with the edition of the crate that the span came from. +See [Macro hygiene] in the Edition Guide for a user-centric description of what this means. + +You should normally use the edition from the token span instead of looking at the global `Session` +edition. +For example, use `span.edition().at_least_rust_2021()` instead of `sess.at_least_rust_2021()`. +This helps ensure that macros behave correctly when used across crates. + +[Macro hygiene]: https://doc.rust-lang.org/nightly/edition-guide/editions/advanced-migrations.html#macro-hygiene + +## Lints + +Lints support a few different options for interacting with editions. +Lints can be *future incompatible edition migration lints*, which are used to support +[migrations][migration lints] to newer editions. +Alternatively, lints can be [edition-specific](#edition-specific-lints), where they change their +default level starting in a specific edition. + +### Migration lints +[migration lints]: #migration-lints +[migration lint]: #migration-lints + +*Migration lints* are used to migrate projects from one edition to the next. +They are implemented with a `MachineApplicable` [suggestion](../diagnostics.md#suggestions) which +will rewrite code so that it will **successfully compile in both the previous and the next +edition**. +For example, the [`keyword_idents`] lint will take identifiers that conflict with a new keyword to +use the raw identifier syntax to avoid the conflict (for example changing `async` to `r#async`). + +Migration lints must be declared with the [`FutureIncompatibilityReason::EditionError`] or +[`FutureIncompatibilityReason::EditionSemanticsChange`] [future-incompatible +option](../diagnostics.md#future-incompatible-lints) in the lint declaration: + +```rust,ignore +declare_lint! { + pub KEYWORD_IDENTS, + Allow, + "detects edition keywords being used as an identifier", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2018), + reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>", + }; +} +``` + +When declared like this, the lint is automatically added to the appropriate +`rust-20xx-compatibility` lint group. +When a user runs `cargo fix --edition`, cargo will pass the `--force-warn rust-20xx-compatibility` +flag to force all of these lints to appear during the edition migration. +Cargo also passes `--cap-lints=allow` so that no other lints interfere with the edition migration. + +Migration lints can be either `Allow` or `Warn` by default. +If it is `Allow`, users usually won't see this warning unless they are doing an edition migration +manually or there is a problem during the migration. +Most migration lints are `Allow`. + +If it is `Warn` by default, users on all editions will see this warning. +Only use `Warn` if you think it is important for everyone to be aware of the change, and to +encourage people to update their code on all editions. +Beware that new warn-by-default lint that hit many projects can be very disruptive and frustrating +for users. +You may consider switching an `Allow` to `Warn` several years after the edition stabilizes. +This will only show up for the relatively small number of stragglers who have not updated to the new +edition. + +[`keyword_idents`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/allowed-by-default.html#keyword-idents +[`FutureIncompatibilityReason::EditionError`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.FutureIncompatibilityReason.html#variant.EditionError +[`FutureIncompatibilityReason::EditionSemanticsChange`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.FutureIncompatibilityReason.html#variant.EditionSemanticsChange + +### Edition-specific lints + +Lints can be marked so that they have a different level starting in a specific edition. +In the lint declaration, use the `@edition` marker: + +```rust,ignore +declare_lint! { + pub SOME_LINT_NAME, + Allow, + "my lint description", + @edition Edition2024 => Warn; +} +``` + +Here, `SOME_LINT_NAME` defaults to `Allow` on all editions before 2024, and then becomes `Warn` +afterwards. + +This should generally be used sparingly, as there are other options: + +- Small impact stylistic changes unrelated to an edition can just make the lint `Warn` on all + editions. If you want people to adopt a different way to write things, then go ahead and commit to + having it show up for all projects. + + Beware that if a new warn-by-default lint hits many projects, it can be very disruptive and + frustrating for users. + +- Change the new style to be a hard error in the new edition, and use a [migration lint] to + automatically convert projects to the new style. For example, + [`ellipsis_inclusive_range_patterns`] is a hard error in 2021, and warns in all previous editions. + + Beware that these cannot be added after the edition stabilizes. + +- Migration lints can also change over time. + For example, the migration lint can start out as `Allow` by default. + For people performing the migration, they will automatically get updated to the new code. + Then, after some years, the lint can be made to `Warn` in previous editions. + + For example [`anonymous_parameters`] was a 2018 Edition migration lint (and a hard-error in 2018) + that was `Allow` by default in previous editions. + Then, three years later, it was changed to `Warn` for all previous editions, so that all users got + a warning that the style was being phased out. + If this was a warning from the start, it would have impacted many projects and be very disruptive. + By making it part of the edition, most users eventually updated to the new edition and were + handled by the migration. + Switching to `Warn` only impacted a few stragglers who did not update. + +[`ellipsis_inclusive_range_patterns`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#ellipsis-inclusive-range-patterns +[`anonymous_parameters`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#anonymous-parameters + +### Lints and stability + +Lints can be marked as being unstable, which can be helpful when developing a new edition feature, +and you want to test out a migration lint. +The feature gate can be specified in the lint's declaration like this: + +```rust,ignore +declare_lint! { + pub SOME_LINT_NAME, + Allow, + "my cool lint", + @feature_gate = sym::my_feature_name; +} +``` + +Then, the lint will only fire if the user has the appropriate `#![feature(my_feature_name)]`. +Just beware that when it comes time to do crater runs testing the migration that the feature gate +will need to be removed. + +Alternatively, you can implement an allow-by-default [migration lint] for an upcoming unstable +edition without a feature gate. +Although users may technically be able to enable the lint before the edition is stabilized, most +will not notice the new lint exists, and it should not disrupt anything or cause any breakage. + +### Idiom lints + +In the 2018 edition, there was a concept of "idiom lints" under the `rust-2018-idioms` lint group. +The concept was to have new idiomatic styles under a different lint group separate from the forced +migrations under the `rust-2018-compatibility` lint group, giving some flexibility as to how people +opt-in to certain edition changes. + +Overall this approach did not seem to work very well, +and it is unlikely that we will use the idiom groups in the future. + +## Standard library changes + +### Preludes + +Each edition comes with a specific prelude of the standard library. +These are implemented as regular modules in [`core::prelude`] and [`std::prelude`]. +New items can be added to the prelude, just beware that this can conflict with user's pre-existing +code. +Usually a [migration lint] should be used to migrate existing code to avoid the conflict. +For example, [`rust_2021_prelude_collisions`] is used to handle the collisions with the new traits +in 2021. + +[`core::prelude`]: https://doc.rust-lang.org/core/prelude/index.html +[`std::prelude`]: https://doc.rust-lang.org/std/prelude/index.html +[`rust_2021_prelude_collisions`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/allowed-by-default.html#rust-2021-prelude-collisions + +### Customized language behavior + +Usually it is not possible to make breaking changes to the standard library. +In some rare cases, the teams may decide that the behavior change is important enough to break this +rule. +The downside is that this requires special handling in the compiler to be able to distinguish when +the old and new signatures or behaviors should be used. + +One example is the change in method resolution for [`into_iter()` of arrays][into-iter]. +This was implemented with the `#[rustc_skip_array_during_method_dispatch]` attribute on the +`IntoIterator` trait which then tells the compiler to consider an alternate trait resolution choice +based on the edition. + +Another example is the [`panic!` macro changes][panic-macro]. +This required defining multiple panic macros, and having the built-in panic macro implementation +determine the appropriate way to expand it. +This also included the [`non_fmt_panics`] [migration lint] to adjust old code to the new form, which +required the `rustc_diagnostic_item` attribute to detect the usage of the panic macro. + +In general it is recommended to avoid these special cases except for very high value situations. + +[into-iter]: https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html +[panic-macro]: https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html +[`non_fmt_panics`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#non-fmt-panics diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing_new_features.md index 427589dab..0140c09bb 100644 --- a/src/doc/rustc-dev-guide/src/implementing_new_features.md +++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md @@ -128,10 +128,10 @@ a new unstable feature: ```rust ignore /// description of feature - (unstable, $feature_name, "CURRENT_RUSTC_VERSION", Some($tracking_issue_number), $edition) + (unstable, $feature_name, "CURRENT_RUSTC_VERSION", Some($tracking_issue_number)) ``` - where `$edition` has the type `Option<Edition>`, and is typically just `None`. If you haven't yet + If you haven't yet opened a tracking issue (e.g. because you want initial feedback on whether the feature is likely to be accepted), you can temporarily use `None` - but make sure to update it before the PR is merged! diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index fd215e3e9..a21254946 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -273,20 +273,22 @@ since it will not be called), and adds a new `FunctionCoverage`, with ## Testing LLVM Coverage -[(See also the compiletest documentation for the `tests/coverage-map` and -`tests/run-coverage` test suites.)](./tests/compiletest.md#coverage-tests) +[(See also the compiletest documentation for the `tests/coverage` +test suite.)](./tests/compiletest.md#coverage-tests) Coverage instrumentation in the MIR is validated by a `mir-opt` test: [`tests/mir-opt/instrument_coverage.rs`]. -Coverage instrumentation in LLVM IR is validated by the [`tests/coverage-map`] -test suite. These tests compile a test program to LLVM IR assembly, and then +Coverage instrumentation in LLVM IR is validated by the [`tests/coverage`] +test suite in `coverage-map` mode. +These tests compile a test program to LLVM IR assembly, and then use the [`src/tools/coverage-dump`] tool to extract and pretty-print the coverage mappings that would be embedded in the final binary. End-to-end testing of coverage instrumentation and coverage reporting is -performed by the [`tests/run-coverage`] and [`tests/run-coverage-rustdoc`] -test suites. These tests compile and run a test program with coverage +performed by the [`tests/coverage`] test suite in `coverage-run` mode, +and by the [`tests/coverage-run-rustdoc`] test suite. +These tests compile and run a test program with coverage instrumentation, then use LLVM tools to convert the coverage data into a human-readable coverage report. @@ -296,19 +298,19 @@ expected LLVM IR instructions and structured data for a coverage-enabled program, including various checks for Coverage Map-related metadata and the LLVM intrinsic calls to increment the runtime counters. -Expected results for the `coverage-map`, `run-coverage`, `run-coverage-rustdoc`, +Expected results for the `coverage`, `coverage-run-rustdoc`, and `mir-opt` tests can be refreshed by running: ```shell -./x test tests/*coverage* --bless +./x test coverage --bless +./x test coverage-run-rustdoc --bless ./x test tests/mir-opt --bless ``` [`tests/mir-opt/instrument_coverage.rs`]: https://github.com/rust-lang/rust/blob/master/tests/mir-opt/instrument_coverage.rs -[`tests/coverage-map`]: https://github.com/rust-lang/rust/tree/master/tests/coverage-map +[`tests/coverage`]: https://github.com/rust-lang/rust/tree/master/tests/coverage [`src/tools/coverage-dump`]: https://github.com/rust-lang/rust/tree/master/src/tools/coverage-dump -[`tests/run-coverage`]: https://github.com/rust-lang/rust/tree/master/tests/run-coverage -[`tests/run-coverage-rustdoc`]: https://github.com/rust-lang/rust/tree/master/tests/run-coverage-rustdoc +[`tests/coverage-run-rustdoc`]: https://github.com/rust-lang/rust/tree/master/tests/coverage-run-rustdoc [`coverage-llvmir`]: https://github.com/rust-lang/rust/tree/master/tests/run-make/coverage-llvmir ## Implementation Details of the `InstrumentCoverage` MIR Pass diff --git a/src/doc/rustc-dev-guide/src/notification-groups/arm.md b/src/doc/rustc-dev-guide/src/notification-groups/arm.md index 4514f8ef3..3abc32c68 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/arm.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/arm.md @@ -1,6 +1,7 @@ # ARM notification group -**Github Label:** [O-ARM] +**Github Label:** [O-ARM] <br> +**Ping command:** `@rustbot ping arm` [O-ARM]: https://github.com/rust-lang/rust/labels/O-ARM diff --git a/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md b/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md index 707334304..9cf4e512c 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md @@ -1,6 +1,7 @@ # Cleanup Crew -**Github Label:** [ICEBreaker-Cleanup-Crew] +**Github Label:** [ICEBreaker-Cleanup-Crew] <br> +**Ping command:** `@rustbot ping cleanup-crew` [ICEBreaker-Cleanup-Crew]: https://github.com/rust-lang/rust/labels/ICEBreaker-Cleanup-Crew diff --git a/src/doc/rustc-dev-guide/src/notification-groups/llvm.md b/src/doc/rustc-dev-guide/src/notification-groups/llvm.md index 69edd8b54..2eff63713 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/llvm.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/llvm.md @@ -1,6 +1,7 @@ # LLVM Notification group -**Github Label:** [A-LLVM] +**Github Label:** [A-LLVM] <br> +**Ping command:** `@rustbot ping llvm` [A-LLVM]: https://github.com/rust-lang/rust/labels/A-LLVM diff --git a/src/doc/rustc-dev-guide/src/notification-groups/risc-v.md b/src/doc/rustc-dev-guide/src/notification-groups/risc-v.md index 15ce1cfc6..1b31297b6 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/risc-v.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/risc-v.md @@ -1,6 +1,7 @@ # RISC-V notification group -**Github Label:** [O-riscv] +**Github Label:** [O-riscv] <br> +**Ping command:** `@rustbot ping risc-v` [O-riscv]: https://github.com/rust-lang/rust/labels/O-riscv diff --git a/src/doc/rustc-dev-guide/src/notification-groups/windows.md b/src/doc/rustc-dev-guide/src/notification-groups/windows.md index 0483babd8..e615a2cbd 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/windows.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/windows.md @@ -1,6 +1,7 @@ # Windows notification group -**Github Label:** [O-Windows] +**Github Label:** [O-Windows] <br> +**Ping command:** `@rustbot ping windows` [O-Windows]: https://github.com/rust-lang/rust/labels/O-Windows diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 5cacb3013..7b6ed435d 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -67,11 +67,8 @@ The following test suites are available, with links for more information: - `run-make-fulldeps` — `run-make` tests which require a linkable build of `rustc`, or the rust demangler - [`run-pass-valgrind`](#valgrind-tests) — tests run with Valgrind -- [`coverage-map`](#coverage-tests) - tests for coverage maps produced by - coverage instrumentation -- [`run-coverage`](#coverage-tests) - tests that run an instrumented program - and check its coverage report -- [`run-coverage-rustdoc`](#coverage-tests) - coverage tests that also run +- [`coverage`](#coverage-tests) - tests for coverage instrumentation +- [`coverage-run-rustdoc`](#coverage-tests) - coverage tests that also run instrumented doctests - [Rustdoc tests](../rustdoc.md#tests): - `rustdoc` — tests for rustdoc, making sure that the generated files @@ -402,7 +399,28 @@ These may be removed in the future. ### Coverage tests -The tests in [`tests/coverage-map`] test the mappings between source code +The tests in [`tests/coverage`] are shared by multiple test modes that test +coverage instrumentation in different ways. +Running the `coverage` test suite will automatically run each test in all of +the different coverage modes. + +Each mode also has an alias to run the coverage tests in just that mode: + +```bash +./x test coverage # runs all of tests/coverage in all coverage modes +./x test tests/coverage # same as above + +./x test tests/coverage/if.rs # runs the specified test in all coverage modes + +./x test coverage-map # runs all of tests/coverage in "coverage-map" mode only +./x test coverage-run # runs all of tests/coverage in "coverage-run" mode only + +./x test coverage-map -- tests/coverage/if.rs # runs the specified test in "coverage-map" mode only +``` + +--- + +In `coverage-map` mode, these tests verify the mappings between source code regions and coverage counters that are emitted by LLVM. They compile the test with `--emit=llvm-ir`, then use a custom tool ([`src/tools/coverage-dump`]) @@ -416,18 +434,18 @@ coverage reports. As a rule of thumb, any PR that doesn't change coverage-specific code should **feel free to re-bless** the `coverage-map` tests as necessary, -without worrying about the actual changes, as long as the `run-coverage` tests +without worrying about the actual changes, as long as the `coverage-run` tests still pass. --- -The tests in [`tests/run-coverage`] perform an end-to-end test of coverage reporting. +In `coverage-run` mode, these tests perform an end-to-end test of coverage reporting. They compile a test program with coverage instrumentation, run that program to produce raw coverage data, and then use LLVM tools to process that data into a human-readable code coverage report. Instrumented binaries need to be linked against the LLVM profiler runtime, -so `run-coverage` tests are **automatically skipped** +so `coverage-run` tests are **automatically skipped** unless the profiler runtime is enabled in `config.toml`: ```toml @@ -439,14 +457,15 @@ profiler = true This also means that they typically don't run in PR CI jobs, though they do run as part of the full set of CI jobs used for merging. -The tests in [`tests/run-coverage-rustdoc`] also run instrumented doctests and +--- + +The tests in [`tests/coverage-run-rustdoc`] also run instrumented doctests and include them in the coverage report. This avoids having to build rustdoc when -only running the main `run-coverage` suite. +only running the main `coverage` suite. -[`tests/coverage-map`]: https://github.com/rust-lang/rust/tree/master/tests/coverage-map +[`tests/coverage`]: https://github.com/rust-lang/rust/tree/master/tests/coverage [`src/tools/coverage-dump`]: https://github.com/rust-lang/rust/tree/master/src/tools/coverage-dump -[`tests/run-coverage`]: https://github.com/rust-lang/rust/tree/master/tests/run-coverage -[`tests/run-coverage-rustdoc`]: https://github.com/rust-lang/rust/tree/master/tests/run-coverage-rustdoc +[`tests/coverage-run-rustdoc`]: https://github.com/rust-lang/rust/tree/master/tests/coverage-run-rustdoc ## Building auxiliary crates diff --git a/src/doc/rustc-dev-guide/src/tests/running.md b/src/doc/rustc-dev-guide/src/tests/running.md index 71fb82b68..7c68dd03f 100644 --- a/src/doc/rustc-dev-guide/src/tests/running.md +++ b/src/doc/rustc-dev-guide/src/tests/running.md @@ -320,3 +320,36 @@ communicate with the server to coordinate running tests (see [remote-test-server]: https://github.com/rust-lang/rust/tree/master/src/tools/remote-test-server [src/bootstrap/test.rs]: https://github.com/rust-lang/rust/tree/master/src/bootstrap/test.rs +## Running rustc_codegen_gcc tests + +First thing to know is that it only supports linux x86_64 at the moment. We will +extend its support later on. + +You need to update `codegen-backends` value in your `config.toml` file in the +`[rust]` section and add "gcc" in the array: + +```toml +codegen-backends = ["llvm", "gcc"] +``` + +Then you need to install libgccjit 12. For example with `apt`: + +```bash +$ apt install libgccjit-12-dev +``` + +Now you can run the following command: + +```bash +$ ./x.py test compiler/rustc_codegen_gcc/ +``` + +If it cannot find the `.so` library (if you installed it with `apt` for example), you +need to pass the library file path with `LIBRARY_PATH`: + +```bash +$ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/ ./x.py test compiler/rustc_codegen_gcc/ +``` + +If you encounter bugs or problems, don't hesitate to open issues on +[rustc_codegen_gcc repository](github.com/rust-lang/rustc_codegen_gcc/). diff --git a/src/doc/rustc-dev-guide/src/traits/canonicalization.md b/src/doc/rustc-dev-guide/src/traits/canonicalization.md new file mode 100644 index 000000000..4c7575f62 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/traits/canonicalization.md @@ -0,0 +1,260 @@ +# Canonicalization + +> **NOTE**: FIXME: The content of this chapter has some overlap with +> [Next-gen trait solving Canonicalization chapter](../solve/canonicalization.html). +> It is suggested to reorganize these contents in the future. + +Canonicalization is the process of **isolating** an inference value +from its context. It is a key part of implementing +[canonical queries][cq], and you may wish to read the parent chapter +to get more context. + +Canonicalization is really based on a very simple concept: every +[inference variable](../type-inference.html#vars) is always in one of +two states: either it is **unbound**, in which case we don't know yet +what type it is, or it is **bound**, in which case we do. So to +isolate some data-structure T that contains types/regions from its +environment, we just walk down and find the unbound variables that +appear in T; those variables get replaced with "canonical variables", +starting from zero and numbered in a fixed order (left to right, for +the most part, but really it doesn't matter as long as it is +consistent). + +[cq]: ./canonical-queries.html + +So, for example, if we have the type `X = (?T, ?U)`, where `?T` and +`?U` are distinct, unbound inference variables, then the canonical +form of `X` would be `(?0, ?1)`, where `?0` and `?1` represent these +**canonical placeholders**. Note that the type `Y = (?U, ?T)` also +canonicalizes to `(?0, ?1)`. But the type `Z = (?T, ?T)` would +canonicalize to `(?0, ?0)` (as would `(?U, ?U)`). In other words, the +exact identity of the inference variables is not important – unless +they are repeated. + +We use this to improve caching as well as to detect cycles and other +things during trait resolution. Roughly speaking, the idea is that if +two trait queries have the same canonical form, then they will get +the same answer. That answer will be expressed in terms of the +canonical variables (`?0`, `?1`), which we can then map back to the +original variables (`?T`, `?U`). + +## Canonicalizing the query + +To see how it works, imagine that we are asking to solve the following +trait query: `?A: Foo<'static, ?B>`, where `?A` and `?B` are unbound. +This query contains two unbound variables, but it also contains the +lifetime `'static`. The trait system generally ignores all lifetimes +and treats them equally, so when canonicalizing, we will *also* +replace any [free lifetime](../appendix/background.html#free-vs-bound) with a +canonical variable (Note that `'static` is actually a _free_ lifetime +variable here. We are not considering it in the typing context of the whole +program but only in the context of this trait reference. Mathematically, we +are not quantifying over the whole program, but only this obligation). +Therefore, we get the following result: + +```text +?0: Foo<'?1, ?2> +``` + +Sometimes we write this differently, like so: + +```text +for<T,L,T> { ?0: Foo<'?1, ?2> } +``` + +This `for<>` gives some information about each of the canonical +variables within. In this case, each `T` indicates a type variable, +so `?0` and `?2` are types; the `L` indicates a lifetime variable, so +`?1` is a lifetime. The `canonicalize` method *also* gives back a +`CanonicalVarValues` array OV with the "original values" for each +canonicalized variable: + +```text +[?A, 'static, ?B] +``` + +We'll need this vector OV later, when we process the query response. + +## Executing the query + +Once we've constructed the canonical query, we can try to solve it. +To do so, we will wind up creating a fresh inference context and +**instantiating** the canonical query in that context. The idea is that +we create a substitution S from the canonical form containing a fresh +inference variable (of suitable kind) for each canonical variable. +So, for our example query: + +```text +for<T,L,T> { ?0: Foo<'?1, ?2> } +``` + +the substitution S might be: + +```text +S = [?A, '?B, ?C] +``` + +We can then replace the bound canonical variables (`?0`, etc) with +these inference variables, yielding the following fully instantiated +query: + +```text +?A: Foo<'?B, ?C> +``` + +Remember that substitution S though! We're going to need it later. + +OK, now that we have a fresh inference context and an instantiated +query, we can go ahead and try to solve it. The trait solver itself is +explained in more detail in [another section](./slg.html), but +suffice to say that it will compute a [certainty value][cqqr] (`Proven` or +`Ambiguous`) and have side-effects on the inference variables we've +created. For example, if there were only one impl of `Foo`, like so: + +[cqqr]: ./canonical-queries.html#query-response + +```rust,ignore +impl<'a, X> Foo<'a, X> for Vec<X> +where X: 'a +{ ... } +``` + +then we might wind up with a certainty value of `Proven`, as well as +creating fresh inference variables `'?D` and `?E` (to represent the +parameters on the impl) and unifying as follows: + +- `'?B = '?D` +- `?A = Vec<?E>` +- `?C = ?E` + +We would also accumulate the region constraint `?E: '?D`, due to the +where clause. + +In order to create our final query result, we have to "lift" these +values out of the query's inference context and into something that +can be reapplied in our original inference context. We do that by +**re-applying canonicalization**, but to the **query result**. + +## Canonicalizing the query result + +As discussed in [the parent section][cqqr], most trait queries wind up +with a result that brings together a "certainty value" `certainty`, a +result substitution `var_values`, and some region constraints. To +create this, we wind up re-using the substitution S that we created +when first instantiating our query. To refresh your memory, we had a query + +```text +for<T,L,T> { ?0: Foo<'?1, ?2> } +``` + +for which we made a substutition S: + +```text +S = [?A, '?B, ?C] +``` + +We then did some work which unified some of those variables with other things. +If we "refresh" S with the latest results, we get: + +```text +S = [Vec<?E>, '?D, ?E] +``` + +These are precisely the new values for the three input variables from +our original query. Note though that they include some new variables +(like `?E`). We can make those go away by canonicalizing again! We don't +just canonicalize S, though, we canonicalize the whole query response QR: + +```text +QR = { + certainty: Proven, // or whatever + var_values: [Vec<?E>, '?D, ?E] // this is S + region_constraints: [?E: '?D], // from the impl + value: (), // for our purposes, just (), but + // in some cases this might have + // a type or other info +} +``` + +The result would be as follows: + +```text +Canonical(QR) = for<T, L> { + certainty: Proven, + var_values: [Vec<?0>, '?1, ?0] + region_constraints: [?0: '?1], + value: (), +} +``` + +(One subtle point: when we canonicalize the query **result**, we do not +use any special treatment for free lifetimes. Note that both +references to `'?D`, for example, were converted into the same +canonical variable (`?1`). This is in contrast to the original query, +where we canonicalized every free lifetime into a fresh canonical +variable.) + +Now, this result must be reapplied in each context where needed. + +## Processing the canonicalized query result + +In the previous section we produced a canonical query result. We now have +to apply that result in our original context. If you recall, way back in the +beginning, we were trying to prove this query: + +```text +?A: Foo<'static, ?B> +``` + +We canonicalized that into this: + +```text +for<T,L,T> { ?0: Foo<'?1, ?2> } +``` + +and now we got back a canonical response: + +```text +for<T, L> { + certainty: Proven, + var_values: [Vec<?0>, '?1, ?0] + region_constraints: [?0: '?1], + value: (), +} +``` + +We now want to apply that response to our context. Conceptually, how +we do that is to (a) instantiate each of the canonical variables in +the result with a fresh inference variable, (b) unify the values in +the result with the original values, and then (c) record the region +constraints for later. Doing step (a) would yield a result of + +```text +{ + certainty: Proven, + var_values: [Vec<?C>, '?D, ?C] + ^^ ^^^ fresh inference variables + region_constraints: [?C: '?D], + value: (), +} +``` + +Step (b) would then unify: + +```text +?A with Vec<?C> +'static with '?D +?B with ?C +``` + +And finally the region constraint of `?C: 'static` would be recorded +for later verification. + +(What we *actually* do is a mildly optimized variant of that: Rather +than eagerly instantiating all of the canonical values in the result +with variables, we instead walk the vector of values, looking for +cases where the value is just a canonical variable. In our example, +`values[2]` is `?C`, so that means we can deduce that `?C := ?B` and +`'?D := 'static`. This gives us a partial set of values. Anything for +which we do not find a value, we create an inference variable.) + diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md index 5e22f10e4..b8311b19a 100644 --- a/src/doc/rustc-dev-guide/src/walkthrough.md +++ b/src/doc/rustc-dev-guide/src/walkthrough.md @@ -181,7 +181,7 @@ gate is removed when the feature is stabilized. make your changes/improvements. When you open a PR on the [rust-lang/rust], a bot will assign your PR to a -review. If there is a particular Rust team member you are working with, you can +reviewer. If there is a particular Rust team member you are working with, you can request that reviewer by leaving a comment on the thread with `r? @reviewer-github-id` (e.g. `r? @eddyb`). If you don't know who to request, don't request anyone; the bot will assign someone automatically based on which files you changed. |