From 629784f943eb73812255013f49264c483b5cbe0a Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 15 Aug 2026 17:35:26 +0200 Subject: [PATCH 01/22] some room to breath --- src/tests/compiletest.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tests/compiletest.md b/src/tests/compiletest.md index 8ec8f9a93b..ae955599b9 100644 --- a/src/tests/compiletest.md +++ b/src/tests/compiletest.md @@ -377,7 +377,7 @@ If you need to work with `#![no_std]` cross-compiling tests, consult the ### Assembly tests The tests in [`tests/assembly-llvm`] test LLVM assembly output. -They compile the test with the `--emit=asm` flag to emit a `.s` file with the assembly output. +They compile the test with the `--emit asm` flag to emit a `.s` file with the assembly output. They then run the LLVM [FileCheck] tool. Each test should be annotated with the `//@ assembly-output:` directive with a @@ -596,7 +596,7 @@ some reason, use the `//@ ignore-coverage-map` or `//@ ignore-coverage-run` dire 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`]) to +They compile the test with `--emit llvm-ir`, then use a custom tool ([`src/tools/coverage-dump`]) to extract and pretty-print the coverage mappings embedded in the IR. These tests don't require the profiler runtime, so they run in PR CI jobs and are easy to run/bless locally. @@ -713,12 +713,12 @@ However, it uses the `--extern` flag to link to the extern crate to make the crate be available as an extern prelude. That allows you to specify the additional syntax of the `--extern` flag, such as renaming a dependency. -For example, `//@ aux-crate:foo=bar.rs` will compile +For example, `//@ aux-crate: foo=bar.rs` will compile `auxiliary/bar.rs` and make it available under then name `foo` within the test. This is similar to how Cargo does dependency renaming. It is also possible to specify [`--extern` modifiers](https://github.com/rust-lang/rust/issues/98405). -For example, `//@ aux-crate:noprelude:foo=bar.rs`. +For example, `//@ aux-crate: noprelude:foo=bar.rs`. `aux-bin` is similar to `aux-build` but will build a binary instead of a library. The binary will be available in `auxiliary/bin` relative to the working directory of the test. @@ -736,7 +736,7 @@ same parent folder as the main test file. However, it also has four additional preset behavior compared to `aux-build` for the proc-macro test auxiliary: -1. The aux test file is built with `--crate-type=proc-macro`. +1. The aux test file is built with `--crate-type proc-macro`. 2. The aux test file is built without `-C prefer-dynamic`, i.e. it will not try to produce a dylib for the aux crate. 3. The aux crate is made available to the test file via extern prelude with From f6c82775599be6889ec423601282516367944e2c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 15 Aug 2026 17:37:32 +0200 Subject: [PATCH 02/22] sembr src/generic-parameters-summary.md --- src/generic-parameters-summary.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/generic-parameters-summary.md b/src/generic-parameters-summary.md index 29a07e297e..47ff4a594a 100644 --- a/src/generic-parameters-summary.md +++ b/src/generic-parameters-summary.md @@ -1,12 +1,15 @@ # Generic parameter definitions -This chapter will discuss how rustc tracks what generic parameters are introduced. For example given some `struct Foo` how does rustc track that `Foo` defines some type parameter `T` (and no other generic parameters). +This chapter will discuss how rustc tracks what generic parameters are introduced. +For example given some `struct Foo` how does rustc track that `Foo` defines some type parameter `T` (and no other generic parameters). This will *not* cover how we track generic parameters introduced via `for<'a>` syntax (e.g. in where clauses or `fn` types), which is covered elsewhere in the [chapter on `Binder`s ][ch_binders]. # `ty::Generics` -The generic parameters introduced by an item are tracked by the [`ty::Generics`] struct. Sometimes items allow usage of generics defined on parent items, this is accomplished via the `ty::Generics` struct having an optional field to specify a parent item to inherit generic parameters of. For example given the following code: +The generic parameters introduced by an item are tracked by the [`ty::Generics`] struct. +Sometimes items allow usage of generics defined on parent items, this is accomplished via the `ty::Generics` struct having an optional field to specify a parent item to inherit generic parameters of. +For example given the following code: ```rust,ignore trait Trait { @@ -14,13 +17,15 @@ trait Trait { } ``` -The `ty::Generics` used for `foo` would contain `[U]` and a parent of `Some(Trait)`. `Trait` would have a `ty::Generics` containing `[Self, T]` with a parent of `None`. +The `ty::Generics` used for `foo` would contain `[U]` and a parent of `Some(Trait)`. +`Trait` would have a `ty::Generics` containing `[Self, T]` with a parent of `None`. The [`GenericParamDef`] struct is used to represent each individual generic parameter in a `ty::Generics` listing. The `GenericParamDef` struct contains information about the generic parameter, for example its name, defid, what kind of parameter it is (i.e. type, const, lifetime). `GenericParamDef` also contains a `u32` index representing what position the parameter is (starting from the outermost parent), this is the value used to represent usages of generic parameters (more on this in the [chapter on representing types][ch_representing_types]). -Interestingly, `ty::Generics` does not currently contain _every_ generic parameter defined on an item. In the case of functions it only contains the _early bound_ parameters. +Interestingly, `ty::Generics` does not currently contain _every_ generic parameter defined on an item. +In the case of functions it only contains the _early bound_ parameters. [ch_representing_types]: ./ty.md [`ty::Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html From 272fa09463012b95a79149b53e01b736ff7effbd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 15 Aug 2026 17:39:33 +0200 Subject: [PATCH 03/22] improve generic-parameters-summary.md --- src/generic-parameters-summary.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/generic-parameters-summary.md b/src/generic-parameters-summary.md index 47ff4a594a..ea9a632784 100644 --- a/src/generic-parameters-summary.md +++ b/src/generic-parameters-summary.md @@ -1,15 +1,17 @@ # Generic parameter definitions This chapter will discuss how rustc tracks what generic parameters are introduced. -For example given some `struct Foo` how does rustc track that `Foo` defines some type parameter `T` (and no other generic parameters). +For example, given some `struct Foo`, +how does rustc track that `Foo` defines some type parameter `T` (and no other generic parameters). This will *not* cover how we track generic parameters introduced via `for<'a>` syntax (e.g. in where clauses or `fn` types), which is covered elsewhere in the [chapter on `Binder`s ][ch_binders]. # `ty::Generics` The generic parameters introduced by an item are tracked by the [`ty::Generics`] struct. -Sometimes items allow usage of generics defined on parent items, this is accomplished via the `ty::Generics` struct having an optional field to specify a parent item to inherit generic parameters of. -For example given the following code: +Sometimes items allow usage of generics defined on parent items, +and this is accomplished via the `ty::Generics` struct having an optional field to specify a parent item to inherit generic parameters of. +For example, given the following code: ```rust,ignore trait Trait { @@ -25,7 +27,7 @@ The [`GenericParamDef`] struct is used to represent each individual generic para `GenericParamDef` also contains a `u32` index representing what position the parameter is (starting from the outermost parent), this is the value used to represent usages of generic parameters (more on this in the [chapter on representing types][ch_representing_types]). Interestingly, `ty::Generics` does not currently contain _every_ generic parameter defined on an item. -In the case of functions it only contains the _early bound_ parameters. +In the case of functions, it only contains the _early bound_ parameters. [ch_representing_types]: ./ty.md [`ty::Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html From 618661287270e3b673ce0f1b241f158b85a62f5f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 15 Aug 2026 17:40:08 +0200 Subject: [PATCH 04/22] typo --- src/tests/compiletest.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/compiletest.md b/src/tests/compiletest.md index ae955599b9..54f23a3c72 100644 --- a/src/tests/compiletest.md +++ b/src/tests/compiletest.md @@ -714,7 +714,7 @@ to link to the extern crate to make the crate be available as an extern prelude. That allows you to specify the additional syntax of the `--extern` flag, such as renaming a dependency. For example, `//@ aux-crate: foo=bar.rs` will compile -`auxiliary/bar.rs` and make it available under then name `foo` within the test. +`auxiliary/bar.rs` and make it available under the name `foo` within the test. This is similar to how Cargo does dependency renaming. It is also possible to specify [`--extern` modifiers](https://github.com/rust-lang/rust/issues/98405). From 08bf7e1ce6ab0df22ed482bc6eb9669b5b1289eb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 15 Aug 2026 17:42:12 +0200 Subject: [PATCH 05/22] avoid inline external links --- src/tests/compiletest.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tests/compiletest.md b/src/tests/compiletest.md index 54f23a3c72..4573a04d02 100644 --- a/src/tests/compiletest.md +++ b/src/tests/compiletest.md @@ -716,8 +716,7 @@ renaming a dependency. For example, `//@ aux-crate: foo=bar.rs` will compile `auxiliary/bar.rs` and make it available under the name `foo` within the test. This is similar to how Cargo does dependency renaming. -It is also possible to -specify [`--extern` modifiers](https://github.com/rust-lang/rust/issues/98405). +It is also possible to specify [`--extern` modifiers]. For example, `//@ aux-crate: noprelude:foo=bar.rs`. `aux-bin` is similar to `aux-build` but will build a binary instead of a library. @@ -905,3 +904,5 @@ Where `N` is the number of threads to use for the parallel frontend, and `M` is Also, when running with `--parallel-frontend-threads`, the `compare-output-by-lines` directive would be implied for all tests, since the output from the parallel frontend can be non-deterministic in terms of the order of lines. The parallel frontend is available in UI tests only at the moment, and is not currently supported in other test suites. + +[`--extern` modifiers]: https://github.com/rust-lang/rust/issues/98405 From 45b5e69ced09b1449f5d561d2ce3b206850d68fc Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 15 Aug 2026 17:43:07 +0200 Subject: [PATCH 06/22] sembr src/bug-fix-procedure.md --- src/bug-fix-procedure.md | 154 ++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/src/bug-fix-procedure.md b/src/bug-fix-procedure.md index ab5e0cd68c..24674a3b89 100644 --- a/src/bug-fix-procedure.md +++ b/src/bug-fix-procedure.md @@ -1,8 +1,8 @@ # Procedures for breaking changes This page defines the best practices procedure for making bug fixes or soundness -corrections in the compiler that can cause existing code to stop compiling. This -text is based on +corrections in the compiler that can cause existing code to stop compiling. +This text is based on [RFC 1589](https://github.com/rust-lang/rfcs/blob/master/text/1589-rustc-bug-fix-procedure.md). # Motivation @@ -10,13 +10,14 @@ text is based on [motivation]: #motivation From time to time, we encounter the need to make a bug fix, soundness -correction, or other change in the compiler which will cause existing code to -stop compiling. When this happens, it is important that we handle the change in -a way that gives users of Rust a smooth transition. What we want to avoid is +correction, or other change in the compiler which will cause existing code to stop compiling. +When this happens, it is important that we handle the change in +a way that gives users of Rust a smooth transition. +What we want to avoid is that existing programs suddenly stop compiling with opaque error messages: we would prefer to have a gradual period of warnings, with clear guidance as to -what the problem is, how to fix it, and why the change was made. This RFC -describes the procedure that we have been developing for handling breaking +what the problem is, how to fix it, and why the change was made. +This RFC describes the procedure that we have been developing for handling breaking changes that aims to achieve that kind of smooth transition. One of the key points of this policy is that (a) warnings should be issued @@ -24,23 +25,23 @@ initially rather than hard errors if at all possible and (b) every change that causes existing code to stop compiling will have an associated tracking issue. This issue provides a point to collect feedback on the results of that change. Sometimes changes have unexpectedly large consequences or there may be a way to -avoid the change that was not considered. In those cases, we may decide to +avoid the change that was not considered. +In those cases, we may decide to change course and roll back the change, or find another solution (if warnings are being used, this is particularly easy to do). ### What qualifies as a bug fix? Note that this RFC does not try to define when a breaking change is permitted. -That is already covered under [RFC 1122][]. This document assumes that the -change being made is in accordance with those policies. Here is a summary of the -conditions from RFC 1122: +That is already covered under [RFC 1122][]. +This document assumes that the change being made is in accordance with those policies. +Here is a summary of the conditions from RFC 1122: - **Soundness changes:** Fixes to holes uncovered in the type system. - **Compiler bugs:** Places where the compiler is not implementing the specified semantics found in an RFC or lang-team decision. - **Underspecified language semantics:** Clarifications to grey areas where the - compiler behaves inconsistently and no formal behavior had been previously - decided. + compiler behaves inconsistently and no formal behavior had been previously decided. Please see [the RFC][rfc 1122] for full details! @@ -53,10 +54,10 @@ described in more detail below): 1. Do a **crater run** to assess the impact of the change. 2. Make a **special tracking issue** dedicated to the change. -3. Do not report an error right away. Instead, **issue forwards-compatibility - lint warnings**. - - Sometimes this is not straightforward. See the text below for suggestions - on different techniques we have employed in the past. +3. Do not report an error right away. + Instead, **issue forwards-compatibility lint warnings**. + - Sometimes this is not straightforward. + See the text below for suggestions on different techniques we have employed in the past. - For cases where warnings are infeasible: - Report errors, but make every effort to give a targeted error message that directs users to the tracking issue @@ -67,27 +68,27 @@ described in more detail below): **stabilize the change**, converting those warnings into errors. Finally, for changes to `rustc_ast` that will affect plugins, the general policy -is to batch these changes. That is discussed below in more detail. +is to batch these changes. +That is discussed below in more detail. ### Tracking issue -Every breaking change should be accompanied by a **dedicated tracking issue** -for that change. The main text of this issue should describe the change being -made, with a focus on what users must do to fix their code. The issue should be -approachable and practical; it may make sense to direct users to an RFC or some -other issue for the full details. The issue also serves as a place where users -can comment with questions or other concerns. +Every breaking change should be accompanied by a **dedicated tracking issue** for that change. +The main text of this issue should describe the change being +made, with a focus on what users must do to fix their code. +The issue should be approachable and practical; it may make sense to direct users to an RFC or some +other issue for the full details. +The issue also serves as a place where users can comment with questions or other concerns. -A template for these breaking-change tracking issues can be found -[here][template]. An example of how such an issue should look can be [found -here][breaking-change-issue]. +A template for these breaking-change tracking issues can be found [here][template]. +An example of how such an issue should look can be [found here][breaking-change-issue]. [template]: https://github.com/rust-lang/rust/issues/new?template=tracking_issue_future.md ### Issuing future compatibility warnings -The best way to handle a breaking change is to begin by issuing -future-compatibility warnings. These are a special category of lint warning. +The best way to handle a breaking change is to begin by issuing future-compatibility warnings. +These are a special category of lint warning. Adding a new future-compatibility warning can be done as follows. ```rust @@ -128,15 +129,15 @@ cx.emit_span_lint( ``` -Finally, register the lint in `compiler/rustc_lint/src/lib.rs`. +Finally, register the lint in `compiler/rustc_lint/src/lib.rs`. There are many examples in that file that already show how to do so. #### Helpful techniques -It can often be challenging to filter out new warnings from older, pre-existing -errors. One technique that has been used in the past is to run the older code -unchanged and collect the errors it would have reported. You can then issue -warnings for any errors you would give which do not appear in that original set. +It can often be challenging to filter out new warnings from older, pre-existing errors. +One technique that has been used in the past is to run the older code +unchanged and collect the errors it would have reported. +You can then issue warnings for any errors you would give which do not appear in that original set. Another option is to abort compilation after the original code completes if errors are reported: then you know that your new code will only execute when there were no errors before. @@ -144,41 +145,43 @@ there were no errors before. #### Crater and crates.io [Crater] is a bot that will compile all crates.io crates and many -public github repos with the compiler with your changes. A report will then be -generated with crates that ceased to compile with or began to compile with your -changes. Crater runs can take a few days to complete. +public github repos with the compiler with your changes. +A report will then be +generated with crates that ceased to compile with or began to compile with your changes. +Crater runs can take a few days to complete. [Crater]: ./tests/crater.md -We should always do a crater run to assess impact. It is polite and considerate -to at least notify the authors of affected crates the breaking change. If we can -submit PRs to fix the problem, so much the better. +We should always do a crater run to assess impact. +It is polite and considerate to at least notify the authors of affected crates the breaking change. +If we can submit PRs to fix the problem, so much the better. #### Is it ever acceptable to go directly to issuing errors? -Changes that are believed to have negligible impact can go directly to issuing -an error. One rule of thumb would be to check against `crates.io`: if fewer than -10 **total** affected projects are found (**not** root errors), we can move -straight to an error. In such cases, we should still make the "breaking change" +Changes that are believed to have negligible impact can go directly to issuing an error. +One rule of thumb would be to check against `crates.io`: if fewer than +10 **total** affected projects are found (**not** root errors), we can move straight to an error. +In such cases, we should still make the "breaking change" page as before, and we should ensure that the error directs users to this page. In other words, everything should be the same except that users are getting an -error, and not a warning. Moreover, we should submit PRs to the affected +error, and not a warning. +Moreover, we should submit PRs to the affected projects (ideally before the PR implementing the change lands in rustc). If the impact is not believed to be negligible (e.g., more than 10 crates are affected), then warnings are required (unless the compiler team agrees to grant -a special exemption in some particular case). If implementing warnings is not +a special exemption in some particular case). +If implementing warnings is not feasible, then we should make an aggressive strategy of migrating crates before -we land the change so as to lower the number of affected crates. Here are some -techniques for approaching this scenario: +we land the change so as to lower the number of affected crates. +Here are some techniques for approaching this scenario: 1. Issue warnings for subparts of the problem, and reserve the new errors for the smallest set of cases you can. 2. Try to give a very precise error message that suggests how to fix the problem and directs users to the tracking issue. 3. It may also make sense to layer the fix: - - First, add warnings where possible and let those land before proceeding to - issue errors. + - First, add warnings where possible and let those land before proceeding to issue errors. - Work with authors of affected crates to ensure that corrected versions are available _before_ the fix lands, so that downstream users can use them. @@ -190,12 +193,11 @@ that we use for unstable features: - After a new release is made, we will go through the outstanding tracking issues corresponding to breaking changes and nominate some of them for **final comment period** (FCP). -- The FCP for such issues lasts for one cycle. In the final week or two of the - cycle, we will review comments and make a final determination: +- The FCP for such issues lasts for one cycle. + In the final week or two of the cycle, we will review comments and make a final determination: - Convert to error: the change should be made into a hard error. - - Revert: we should remove the warning and continue to allow the older code to - compile. + - Revert: we should remove the warning and continue to allow the older code to compile. - Defer: can't decide yet, wait longer, or try other strategies. Ideally, breaking changes should have landed on the **stable branch** of the @@ -206,10 +208,12 @@ compiler before they are finalized. ### Removing a lint Once we have decided to make a "future warning" into a hard error, we need a PR -that removes the custom lint. As an example, here are the steps required to -remove the `overlapping_inherent_impls` compatibility lint. First, convert the -name of the lint to uppercase (`OVERLAPPING_INHERENT_IMPLS`) ripgrep through the -source for that string. We will basically by converting each place where this +that removes the custom lint. +As an example, here are the steps required to +remove the `overlapping_inherent_impls` compatibility lint. +First, convert the name of the lint to uppercase (`OVERLAPPING_INHERENT_IMPLS`) ripgrep through the +source for that string. +We will basically by converting each place where this lint name is mentioned (in the compiler, we use the upper-case name, and a macro automatically generates the lower-case string; so searching for `overlapping_inherent_impls` would not find much). @@ -234,8 +238,9 @@ declare_lint! { } ``` -This `declare_lint!` macro creates the relevant data structures. Remove it. You -will also find that there is a mention of `OVERLAPPING_INHERENT_IMPLS` later in +This `declare_lint!` macro creates the relevant data structures. +Remove it. +You will also find that there is a mention of `OVERLAPPING_INHERENT_IMPLS` later in the file as [part of a `lint_array!`][lintarraysource]; remove it too. [lintarraysource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc/lint/builtin.rs#L252-L290 @@ -255,8 +260,9 @@ where `#36889` is the tracking issue for your lint. Finally, the last class of references you will see are the places that actually **trigger** the lint itself (i.e., what causes the warnings to appear). These -you do not want to delete. Instead, you want to convert them into errors. In -this case, the [`add_lint` call][addlintsource] looks like this: +you do not want to delete. +Instead, you want to convert them into errors. +In this case, the [`add_lint` call][addlintsource] looks like this: ```rust self.tcx.sess.add_lint(lint::builtin::OVERLAPPING_INHERENT_IMPLS, @@ -267,16 +273,16 @@ self.tcx.sess.add_lint(lint::builtin::OVERLAPPING_INHERENT_IMPLS, You'll also often find `node_span_lint` used for this. -We want to convert this into an error. In some cases, there may be an -existing error for this scenario. In others, we will need to allocate a -fresh diagnostic code. [Instructions for allocating a fresh diagnostic +We want to convert this into an error. +In some cases, there may be an existing error for this scenario. +In others, we will need to allocate a fresh diagnostic code. + [Instructions for allocating a fresh diagnostic code can be found here.](./diagnostics/error-codes.md) You may want to mention in the extended description that the compiler behavior -changed on this point, and include a reference to the tracking issue for -the change. +changed on this point, and include a reference to the tracking issue for the change. -Let's say that we've adopted `E0592` as our code. Then we can change the -`add_lint()` call above to something like: +Let's say that we've adopted `E0592` as our code. +Then we can change the `add_lint()` call above to something like: ```rust struct_span_code_err!(self.dcx(), self.tcx.span_of_impl(item1).unwrap(), E0592, msg) @@ -296,9 +302,10 @@ struct MyDiagnostic { #### Update tests -Finally, run the test suite. These should be some tests that used to reference -the `overlapping_inherent_impls` lint, those will need to be updated. In -general, if the test used to have `#[deny(overlapping_inherent_impls)]`, that +Finally, run the test suite. +These should be some tests that used to reference +the `overlapping_inherent_impls` lint, those will need to be updated. +In general, if the test used to have `#[deny(overlapping_inherent_impls)]`, that can just be removed. ``` @@ -307,7 +314,8 @@ can just be removed. #### All done! -Open a PR. =) +Open a PR. +=) [addlintsource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc_typeck/coherence/inherent.rs#L300-L303 [futuresource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc_lint/lib.rs#L202-L205 From 191d52130997f8ba070d16d9e880cc89fdd2cd5f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 15 Aug 2026 18:00:26 +0200 Subject: [PATCH 07/22] improve bug-fix-procedure.md --- src/bug-fix-procedure.md | 44 +++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/bug-fix-procedure.md b/src/bug-fix-procedure.md index 24674a3b89..0db0e07d4e 100644 --- a/src/bug-fix-procedure.md +++ b/src/bug-fix-procedure.md @@ -2,8 +2,7 @@ This page defines the best practices procedure for making bug fixes or soundness corrections in the compiler that can cause existing code to stop compiling. -This text is based on -[RFC 1589](https://github.com/rust-lang/rfcs/blob/master/text/1589-rustc-bug-fix-procedure.md). +This text is based on [RFC 1589]. # Motivation @@ -13,9 +12,8 @@ From time to time, we encounter the need to make a bug fix, soundness correction, or other change in the compiler which will cause existing code to stop compiling. When this happens, it is important that we handle the change in a way that gives users of Rust a smooth transition. -What we want to avoid is -that existing programs suddenly stop compiling with opaque error messages: we -would prefer to have a gradual period of warnings, with clear guidance as to +What we want to avoid is that existing programs suddenly stop compiling with opaque error messages: +we would prefer to have a gradual period of warnings, with clear guidance as to what the problem is, how to fix it, and why the change was made. This RFC describes the procedure that we have been developing for handling breaking changes that aims to achieve that kind of smooth transition. @@ -26,14 +24,14 @@ causes existing code to stop compiling will have an associated tracking issue. This issue provides a point to collect feedback on the results of that change. Sometimes changes have unexpectedly large consequences or there may be a way to avoid the change that was not considered. -In those cases, we may decide to -change course and roll back the change, or find another solution (if warnings -are being used, this is particularly easy to do). +In those cases, +we may decide to change course and roll back the change, +or find another solution (and if warnings are being used, this is particularly easy to do). ### What qualifies as a bug fix? Note that this RFC does not try to define when a breaking change is permitted. -That is already covered under [RFC 1122][]. +That is already covered under [RFC 1122]. This document assumes that the change being made is in accordance with those policies. Here is a summary of the conditions from RFC 1122: @@ -92,7 +90,7 @@ These are a special category of lint warning. Adding a new future-compatibility warning can be done as follows. ```rust -// 1. Define the lint in `compiler/rustc_lint/src/builtin.rs` and +// 1. Define the lint in `compiler/rustc_lint/src/builtin.rs` and // add the metadata for the future incompatibility: declare_lint! { pub YOUR_LINT_HERE, @@ -111,7 +109,7 @@ pub struct MyLintPass { ... } -impl {Early,Late}LintPass for MyLintPass { +impl {Early,Late}LintPass for MyLintPass { ... } @@ -146,15 +144,15 @@ there were no errors before. [Crater] is a bot that will compile all crates.io crates and many public github repos with the compiler with your changes. -A report will then be -generated with crates that ceased to compile with or began to compile with your changes. +A report will then be generated with crates that ceased to compile with, +or began to compile with your changes. Crater runs can take a few days to complete. [Crater]: ./tests/crater.md We should always do a crater run to assess impact. -It is polite and considerate to at least notify the authors of affected crates the breaking change. -If we can submit PRs to fix the problem, so much the better. +It is polite and considerate to notify the authors of crates affected by the breaking change. +It is even better to submit PRs fixing the breakage. #### Is it ever acceptable to go directly to issuing errors? @@ -165,14 +163,14 @@ In such cases, we should still make the "breaking change" page as before, and we should ensure that the error directs users to this page. In other words, everything should be the same except that users are getting an error, and not a warning. -Moreover, we should submit PRs to the affected -projects (ideally before the PR implementing the change lands in rustc). +Moreover, we should submit PRs to the affected projects +(ideally before the PR implementing the change lands in rustc). If the impact is not believed to be negligible (e.g., more than 10 crates are affected), then warnings are required (unless the compiler team agrees to grant a special exemption in some particular case). -If implementing warnings is not -feasible, then we should make an aggressive strategy of migrating crates before +If implementing warnings is not feasible, +then we should make an aggressive strategy of migrating crates before we land the change so as to lower the number of affected crates. Here are some techniques for approaching this scenario: @@ -211,8 +209,8 @@ Once we have decided to make a "future warning" into a hard error, we need a PR that removes the custom lint. As an example, here are the steps required to remove the `overlapping_inherent_impls` compatibility lint. -First, convert the name of the lint to uppercase (`OVERLAPPING_INHERENT_IMPLS`) ripgrep through the -source for that string. +First, convert the name of the lint to uppercase (`OVERLAPPING_INHERENT_IMPLS`); +search the source for that string. We will basically by converting each place where this lint name is mentioned (in the compiler, we use the upper-case name, and a macro automatically generates the lower-case string; so searching for @@ -304,7 +302,7 @@ struct MyDiagnostic { Finally, run the test suite. These should be some tests that used to reference -the `overlapping_inherent_impls` lint, those will need to be updated. +the `overlapping_inherent_impls` lint; those will need to be updated. In general, if the test used to have `#[deny(overlapping_inherent_impls)]`, that can just be removed. @@ -315,7 +313,6 @@ can just be removed. #### All done! Open a PR. -=) [addlintsource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc_typeck/coherence/inherent.rs#L300-L303 [futuresource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc_lint/lib.rs#L202-L205 @@ -324,3 +321,4 @@ Open a PR. [rfc 1122]: https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md [breaking-change-issue]: https://gist.github.com/nikomatsakis/631ec8b4af9a18b5d062d9d9b7d3d967 +[RFC 1589]: https://github.com/rust-lang/rfcs/blob/master/text/1589-rustc-bug-fix-procedure.md From 9c7e292d36fdf18765f099889c3f6486f59e1036 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 15 Aug 2026 18:01:13 +0200 Subject: [PATCH 08/22] sembr src/part-4-intro.md --- src/part-4-intro.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/part-4-intro.md b/src/part-4-intro.md index 6a84331641..db692012cf 100644 --- a/src/part-4-intro.md +++ b/src/part-4-intro.md @@ -1,12 +1,14 @@ # Analysis This part discusses the many analyses that the compiler uses to check various -properties of the code and to inform later stages. Typically, this is what people -mean when they talk about "Rust's type system". This includes the -representation, inference, and checking of types, the trait system, and the -borrow checker. These analyses do not happen as one big pass or set of -contiguous passes. Rather, they are spread out throughout various parts of the -compilation process and use different intermediate representations. For example, +properties of the code and to inform later stages. +Typically, this is what people mean when they talk about "Rust's type system". +This includes the representation, inference, and checking of types, the trait system, and the +borrow checker. +These analyses do not happen as one big pass or set of contiguous passes. +Rather, they are spread out throughout various parts of the +compilation process and use different intermediate representations. +For example, type checking happens on the HIR, while borrow checking happens on the MIR. Nonetheless, for the sake of presentation, we will discuss all of these analyses in this part of the guide. From febbcd633b50516820f8f97589086c891c9834a9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 18 Aug 2026 19:20:29 +0200 Subject: [PATCH 09/22] sembr src/hir-typeck/coercions.md --- src/hir-typeck/coercions.md | 118 ++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 38 deletions(-) diff --git a/src/hir-typeck/coercions.md b/src/hir-typeck/coercions.md index de9f4449bb..011005c92e 100644 --- a/src/hir-typeck/coercions.md +++ b/src/hir-typeck/coercions.md @@ -1,7 +1,9 @@ # Coercions -Coercions are implicit operations which transform a value into a different type. A coercion *site* is a position where a coercion is able to be implicitly performed. There are two kinds of coercion sites: +Coercions are implicit operations which transform a value into a different type. +A coercion *site* is a position where a coercion is able to be implicitly performed. +There are two kinds of coercion sites: - one-to-one - LUB (Least-Upper-Bound) @@ -18,13 +20,15 @@ See the Reference page on coercions for descriptions of what coercions exist and ## one-to-one coercions -With a one-to-one coercion we coerce from one singular type to a known target type. In the above example this would be the coercion from `&mut u32` to `&u32`. +With a one-to-one coercion we coerce from one singular type to a known target type. +In the above example this would be the coercion from `&mut u32` to `&u32`. A one-to-one coercion can be performed by calling [`FnCtxt::coerce`][fnctxt_coerce]. ## LUB coercions -With a LUB coercion we coerce a set of source types to some unknown target type. Unlike one-to-one coercions, a LUB coercion *produces* the target type that all of the source types coerce to. +With a LUB coercion we coerce a set of source types to some unknown target type. +Unlike one-to-one coercions, a LUB coercion *produces* the target type that all of the source types coerce to. In the above example this would be the LUB coercion of both `&mut i32` and `&i32`, where we produce the target type `&i32`. @@ -51,11 +55,14 @@ There are a few key steps here: ### Step 1 -First we create a [`CoerceMany`][coerce_many] value, this stores all of the state required for the LUB coercion. Unlike one-to-one coercions, a LUB coercion isn't a single function call as we want to intermix typechecking with advancing the LUB coercion. +First we create a [`CoerceMany`][coerce_many] value, this stores all of the state required for the LUB coercion. +Unlike one-to-one coercions, a LUB coercion isn't a single function call as we want to intermix typechecking with advancing the LUB coercion. -Creating a `CoerceMany` takes some `initial_lub` type. This is different from the *target* of the coercion which is an output of a LUB coercion rather than an input (unlike a one-to-one coercion). +Creating a `CoerceMany` takes some `initial_lub` type. +This is different from the *target* of the coercion which is an output of a LUB coercion rather than an input (unlike a one-to-one coercion). -The initial lub ty should be derived from the [`Expectation`][expectation] for whatever expression this LUB coercion is for. It allows for inference constraints from computing the LUB coercion to propagate into the `Expectation`s used for type checking later expressions participating in the LUB coercion. +The initial lub ty should be derived from the [`Expectation`][expectation] for whatever expression this LUB coercion is for. +It allows for inference constraints from computing the LUB coercion to propagate into the `Expectation`s used for type checking later expressions participating in the LUB coercion. See the ["unnecessary inference constraints"][unnecessary_inference_constraints] header for some more information about the effects this has. @@ -65,17 +72,20 @@ If there's no `Expectation` to use then some new infer var should be made for th Next, for each expression participating in the LUB coercion, we typecheck it then invoke [`CoerceMany::coerce`][coerce_many_coerce] with its type. -In some cases the expression participating in the LUB coercion doesn't actually exist in the HIR. For example when handling an operand-less `break` or `return` expression we need `()` to participate in the LUB coercion. +In some cases the expression participating in the LUB coercion doesn't actually exist in the HIR. +For example when handling an operand-less `break` or `return` expression we need `()` to participate in the LUB coercion. In these cases the [`CoerceMany::coerce_forced_unit`][coerce_many_coerce_forced_unit] method can be used. -The `CoerceMany::coerce` and `coerce_forced_unit` methods will both emit errors if the new type causes the LUB coercion to be unsatisfiable. In this case the final type of the LUB coercion will be an error type. +The `CoerceMany::coerce` and `coerce_forced_unit` methods will both emit errors if the new type causes the LUB coercion to be unsatisfiable. +In this case the final type of the LUB coercion will be an error type. ### Step 3 Finally once all expressions have been coerced the final type of the LUB coercion can be obtained by calling [`CoerceMany::complete`][coerce_many_complete]. -The resulting type of the LUB coercion is meaningfully different from the initial lub type passed in when constructing the [`CoerceMany`][coerce_many]. You should always take the resulting type of the LUB coercion and perform any necessary checks on it. +The resulting type of the LUB coercion is meaningfully different from the initial lub type passed in when constructing the [`CoerceMany`][coerce_many]. +You should always take the resulting type of the LUB coercion and perform any necessary checks on it. ## Implementation nuances @@ -83,13 +93,15 @@ The resulting type of the LUB coercion is meaningfully different from the initia When a coerce operation succeeds we record what kind of coercion it was, for example an unsize coercion or an autoderef etc. This is handled as part of the coerce operation by writing a list of *adjustments* into the in-progress [`TypeckResults`][typeck_results]. -When building THIR we take the adjustments stored in the `TypeckResults` and make all of the coercion steps explicit. After this point in the compiler there isn't really a notion of coercions, only explicit casts and subtyping in the MIR. +When building THIR we take the adjustments stored in the `TypeckResults` and make all of the coercion steps explicit. +After this point in the compiler there isn't really a notion of coercions, only explicit casts and subtyping in the MIR. TODO: write and link to an adjustments chapter here ### How does `CoerceMany` work -[`CoerceMany`][coerce_many] works by repeatedly taking the current lub ty and some new source type, and computing a new lub ty which both types can coerce to. The core logic of taking a pair of types and computing some new third type can be found in [`try_find_coercion_lub`][try_find_coercion_lub]. +[`CoerceMany`][coerce_many] works by repeatedly taking the current lub ty and some new source type, and computing a new lub ty which both types can coerce to. +The core logic of taking a pair of types and computing some new third type can be found in [`try_find_coercion_lub`][try_find_coercion_lub]. ```rust fn foo() {} @@ -102,19 +114,28 @@ let a = match my_bool { } ``` -In this example when type checking the `match` expression a LUB coercion is performed. This LUB coercion starts out with an initial lub ty of some inference variable `?x` due to the let statement having no known type. +In this example when type checking the `match` expression a LUB coercion is performed. +This LUB coercion starts out with an initial lub ty of some inference variable `?x` due to the let statement having no known type. -There are three expressions that participate in this LUB coercion. The first expression of a LUB coercion is special, instead of computing a new type with the existing initial lub ty, we coerce directly from the first expression to the initial lub ty. +There are three expressions that participate in this LUB coercion. +The first expression of a LUB coercion is special, instead of computing a new type with the existing initial lub ty, we coerce directly from the first expression to the initial lub ty. -1. After type checking `true => foo,` we wind up with the type `FnDef(Foo)`. We then call [`CoerceMany::coerce`][coerce_many_coerce] which will perform a one-to-one coercion of `FnDef(Foo)` to `?x`. This infers `?x=FnDef(Foo)` giving us a new lub ty for the LUB coercion. -2. After type checking `true if other_bool => foo,` we once again wind up with the type `FnDef(Foo)`. We'll then call `CoerceMany::coerce` which will attempt to compute a new lub ty from our previous lub ty (`FnDef(Foo)`) and the type of this expression (`FnDef(Foo)`). This gives us a lub ty of `FnDef(Foo)`. -3. After type checking `false => bar,` we'll wind up with the type `FnDef(Bar)`. We'll then call `CoerceMany::coerce` which will attempt to compute a new lub ty from our previous lub ty (`FnDef(Foo)`) and the type of this expression (`FnDef(Bar)`). In this case we get the type `fn() -> ()` as we choose to coerce both function item types to a function pointer. +1. After type checking `true => foo,` we wind up with the type `FnDef(Foo)`. + We then call [`CoerceMany::coerce`][coerce_many_coerce] which will perform a one-to-one coercion of `FnDef(Foo)` to `?x`. + This infers `?x=FnDef(Foo)` giving us a new lub ty for the LUB coercion. +2. After type checking `true if other_bool => foo,` we once again wind up with the type `FnDef(Foo)`. + We'll then call `CoerceMany::coerce` which will attempt to compute a new lub ty from our previous lub ty (`FnDef(Foo)`) and the type of this expression (`FnDef(Foo)`). + This gives us a lub ty of `FnDef(Foo)`. +3. After type checking `false => bar,` we'll wind up with the type `FnDef(Bar)`. + We'll then call `CoerceMany::coerce` which will attempt to compute a new lub ty from our previous lub ty (`FnDef(Foo)`) and the type of this expression (`FnDef(Bar)`). + In this case we get the type `fn() -> ()` as we choose to coerce both function item types to a function pointer. This gives us a final type for the LUB coercion of `fn() -> ()`. ### Transitive coercions -[`CoerceMany`][coerce_many]'s algorithm of repeatedly attempting to coerce the current target type to the new type currently results in "Transitive Coercions". It's possible for a step in a LUB coercion to coerce an expression, and then a later step to coerce that expression further. +[`CoerceMany`][coerce_many]'s algorithm of repeatedly attempting to coerce the current target type to the new type currently results in "Transitive Coercions". +It's possible for a step in a LUB coercion to coerce an expression, and then a later step to coerce that expression further. ```rust struct Foo; @@ -138,17 +159,23 @@ fn main() { } ``` -Here we have a LUB coercion with an initial lub ty of `?x`. In the first step we do a one-to-one coercion of `&Foo` to `?x` (reminder the first step is special). +Here we have a LUB coercion with an initial lub ty of `?x`. +In the first step we do a one-to-one coercion of `&Foo` to `?x` (reminder the first step is special). -In the second step we compute a new lub ty from the current lub ty of `&Foo` and the new type of `&[u8; 2]`. This new lub ty would be `&[u8; 2]` by performing a deref coercion of `&Foo` to `&[u8; 2]` on the first expression. +In the second step we compute a new lub ty from the current lub ty of `&Foo` and the new type of `&[u8; 2]`. +This new lub ty would be `&[u8; 2]` by performing a deref coercion of `&Foo` to `&[u8; 2]` on the first expression. -In the third step we compute a new lub ty from the current lub ty of `&[u8; 2]` and the new type of `&[u8]`. This new lub ty would be `&[u8]` by performing an unsizing coercion of `&[u8; 2]` to `&[u8]` on the first two expressions. +In the third step we compute a new lub ty from the current lub ty of `&[u8; 2]` and the new type of `&[u8]`. +This new lub ty would be `&[u8]` by performing an unsizing coercion of `&[u8; 2]` to `&[u8]` on the first two expressions. -Note how the first expression is coerced twice. Once a deref coercion from `&Foo` to `&[u8; 2]`, and then an unsizing coercion from `&[u8; 2]` to `&[u8]`. +Note how the first expression is coerced twice. +Once a deref coercion from `&Foo` to `&[u8; 2]`, and then an unsizing coercion from `&[u8; 2]` to `&[u8]`. -The current implementation of transitive coercions is broken, the previous example actually ICEs on stable. While the logic for performing a LUB coercion can produce transitive coercions just fine, the rest of the compiler is not set up to handle them. +The current implementation of transitive coercions is broken, the previous example actually ICEs on stable. +While the logic for performing a LUB coercion can produce transitive coercions just fine, the rest of the compiler is not set up to handle them. -One-to-one coercions are also not capable of producing a lot of the kinds of transitive coercions that LUB coercions can. For example if we take the previous example and turn it into a one-to-one coercion we get a compile error: +One-to-one coercions are also not capable of producing a lot of the kinds of transitive coercions that LUB coercions can. +For example if we take the previous example and turn it into a one-to-one coercion we get a compile error: ```rust struct Foo; @@ -188,15 +215,18 @@ There is likely room for improving the structure of this function to make it mor The implementation of one-to-one coercions is reused as part of LUB coercions. -It would be wrong for LUB coercions to use one way subtyping when relating signatures or falling back to subtyping in the case of no coercions being possible. Instead we want to compute a mutual supertype of the two types. +It would be wrong for LUB coercions to use one way subtyping when relating signatures or falling back to subtyping in the case of no coercions being possible. +Instead we want to compute a mutual supertype of the two types. The `use_lub` field on [`Coerce`][coerce_ty] exists to toggle whether to perform normal subtyping (in the case of a one-to-one coercion), or whether to compute a mutual supertype (in the case of a LUB coercion). ### Lubbing -In theory computing a mutual supertype should be as simple as creating some new infer var `?mutual_sup` and then requiring `lub_ty <: ?mutual_sup` and `new_ty <: ?mutual_sup`. In reality LUB coercions use a special [`TypeRelation`][type_relation], [`LatticeOp`][lattice_op]. +In theory computing a mutual supertype should be as simple as creating some new infer var `?mutual_sup` and then requiring `lub_ty <: ?mutual_sup` and `new_ty <: ?mutual_sup`. +In reality LUB coercions use a special [`TypeRelation`][type_relation], [`LatticeOp`][lattice_op]. -This is primarily to work around subtyping/generalization for higher ranked types being fairly broken. Unlike normal subtyping, when encountering higher ranked types the lub type relation will switch to invariance. +This is primarily to work around subtyping/generalization for higher ranked types being fairly broken. +Unlike normal subtyping, when encountering higher ranked types the lub type relation will switch to invariance. This enforces that the binders of the higher ranked types are equivalent which avoids the need to pick a "most general" binder, which would be quite difficult to do. @@ -204,7 +234,8 @@ It also avoids the process of computing a mutual supertype being *order dependen The current issues with higher ranked types and subtyping would cause this property to not hold if we were to use the naive method of computing a mutual supertype. -Coercions being turned into explicit MIR operations during MIR building means that the process of computing the final type of a LUB coercion only occurs during HIR typeck. This also means the behaviour of computing a mutual supertype only matters for type inference, and is not soundness relevant. +Coercions being turned into explicit MIR operations during MIR building means that the process of computing the final type of a LUB coercion only occurs during HIR typeck. +This also means the behaviour of computing a mutual supertype only matters for type inference, and is not soundness relevant. ## Cautionary notes @@ -214,17 +245,21 @@ Care should be taken when coercing from inside of a probe as both one-to-one coe LUB coercions will emit error when a coercion step fails, this makes it entirely suitable for use inside of probes. -1-to-1 and LUB coercions will both apply *adjustments* to the coerced expressions on success. This means that if inside of a probe and an attempt to coerce succeeds, then the probe must not rollback anything. +1-to-1 and LUB coercions will both apply *adjustments* to the coerced expressions on success. +This means that if inside of a probe and an attempt to coerce succeeds, then the probe must not rollback anything. -It's therefore correct to wrap a [`FnCtxt::coerce`][fnctxt_coerce] call inside of a [`commit_if_ok`][commit_if_ok], but would be wrong to do so if returning `Err` after the coerce call. It would also be wrong to call `FnCtxt::coerce` from within a [`probe`][probe]. +It's therefore correct to wrap a [`FnCtxt::coerce`][fnctxt_coerce] call inside of a [`commit_if_ok`][commit_if_ok], but would be wrong to do so if returning `Err` after the coerce call. +It would also be wrong to call `FnCtxt::coerce` from within a [`probe`][probe]. [`CoerceMany`][coerce_many] should never be used from within a `probe` or `commit_if_ok`. ### Never-to-Any coercions -Coercing from the never type (`!`) to an inference variable will result in a [`NeverToAny`][never_to_any] coercion with a target type of the inference variable. This is subtly different from *unifying* the inference variable with the never type. +Coercing from the never type (`!`) to an inference variable will result in a [`NeverToAny`][never_to_any] coercion with a target type of the inference variable. +This is subtly different from *unifying* the inference variable with the never type. -Unifying some infer var `?x` with `!` requires that `?x` actually be *equal* to `!`. However, a `NeverToAny` coercion allows for `?x` to be inferred to any possible type. +Unifying some infer var `?x` with `!` requires that `?x` actually be *equal* to `!`. +However, a `NeverToAny` coercion allows for `?x` to be inferred to any possible type. This distinction means that in cases where the initial lub ty of a coercion is an inference variable (e.g. there's no [`Expectation`][expectation] to use for the initial lub ty), it's still important to use a coercion instead of subtyping. @@ -234,13 +269,16 @@ See PR [#147834](https://github.com/rust-lang/rust/pull/147834) which fixes a bu Even though subtyping is not a coercion, both [`FnCtxt::coerce`][fnctxt_coerce] and [`CoerceMany::coerce`][coerce_many_coerce]/[`coerce_forced_unit`][coerce_many_coerce_forced_unit] are able to succeed due to subtyping. -For one-to-one coercions we will try to enforce the source type is a subtype of the target type. For LUB coercions we will try to compute a type that is a supertype of all the existing types. +For one-to-one coercions we will try to enforce the source type is a subtype of the target type. +For LUB coercions we will try to compute a type that is a supertype of all the existing types. -For example performing a one-to-one coercion of `?x` to `u32` will fallback to subtyping, inferring `?x eq u32`. This means that when a coercion fails there's no need to attempt subtyping afterwards. +For example performing a one-to-one coercion of `?x` to `u32` will fallback to subtyping, inferring `?x eq u32`. +This means that when a coercion fails there's no need to attempt subtyping afterwards. ### Unnecessary inference constraints -Using types from [`Expectation`][expectation]s as the initial lub ty can cause infer vars to be constrained by the types of the expressions participating in the LUB coercion. This is not always desirable as these infer vars actually only need to be constrained by the final type of the LUB coercion. +Using types from [`Expectation`][expectation]s as the initial lub ty can cause infer vars to be constrained by the types of the expressions participating in the LUB coercion. +This is not always desirable as these infer vars actually only need to be constrained by the final type of the LUB coercion. ```rust fn foo(_: T) {} @@ -254,19 +292,23 @@ foo::(match my_bool { }) ``` -Here we have a LUB coercion with the first expression being of type `FnDef(a)` and the second expression being of type `FnDef(b)`. If we use `?x` as the initial lub ty of the LUB coercion then we would get the following behaviour: +Here we have a LUB coercion with the first expression being of type `FnDef(a)` and the second expression being of type `FnDef(b)`. +If we use `?x` as the initial lub ty of the LUB coercion then we would get the following behaviour: - expression 1: infer `?x=FnDef(a)` - expression 2: find a coercion lub between `FnDef(a), FnDef(b)` resulting in `fn() -> ()` -- the final type of the LUB coercion is `fn() -> ()`. equate `?x eq fn() -> ()`, where `?x` actually already has been inferred to `FnDef(a)`, so this is actually equating `FnDef(a) eq fn() -> ()` which does not hold +- the final type of the LUB coercion is `fn() -> ()`. + equate `?x eq fn() -> ()`, where `?x` actually already has been inferred to `FnDef(a)`, so this is actually equating `FnDef(a) eq fn() -> ()` which does not hold -To avoid some (but not all) of these undesirable inference constraints, if the `Expectation` for the LUB coercion is an inference variable then we won't use it as the initial lub ty. Instead we create a new infer var, for example in the above code snippet we would actually make some new infer var `?y` for the initial lub ty instead of using `?x`. +To avoid some (but not all) of these undesirable inference constraints, if the `Expectation` for the LUB coercion is an inference variable then we won't use it as the initial lub ty. +Instead we create a new infer var, for example in the above code snippet we would actually make some new infer var `?y` for the initial lub ty instead of using `?x`. - expression 1: infer `?y=FnDef(a)` - expression 2: find a coercion lub between `FnDef(a), FnDef(b)` resulting in `fn() -> ()` - the final type of the LUB coercion is `fn() -> ()`, infer `?x=fn() -> ()` See [#140283](https://github.com/rust-lang/rust/pull/140283) for a case where we had undesirable inference constraints caused by not creating a new infer var. -This doesn't avoid unnecessary constraints in *all* cases, only the most common case of having an infer var as our `Expectation`. In theory it would be desirable to avoid these constraints in all cases but it would be quite involved to do so. +This doesn't avoid unnecessary constraints in *all* cases, only the most common case of having an infer var as our `Expectation`. +In theory it would be desirable to avoid these constraints in all cases but it would be quite involved to do so. [coerce_many]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/coercion/struct.CoerceMany.html [coerce_many_coerce]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/coercion/struct.CoerceMany.html#method.coerce From 31eeda50c82380c31867f3c66f4af10c85f4b6fd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:05:36 +0200 Subject: [PATCH 10/22] improve hir-typeck/coercions.md --- src/hir-typeck/coercions.md | 78 ++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/src/hir-typeck/coercions.md b/src/hir-typeck/coercions.md index 011005c92e..b977e8f308 100644 --- a/src/hir-typeck/coercions.md +++ b/src/hir-typeck/coercions.md @@ -1,6 +1,6 @@ # Coercions - + Coercions are implicit operations which transform a value into a different type. A coercion *site* is a position where a coercion is able to be implicitly performed. There are two kinds of coercion sites: @@ -20,17 +20,18 @@ See the Reference page on coercions for descriptions of what coercions exist and ## one-to-one coercions -With a one-to-one coercion we coerce from one singular type to a known target type. +With a one-to-one coercion, we coerce from one singular type to a known target type. In the above example this would be the coercion from `&mut u32` to `&u32`. A one-to-one coercion can be performed by calling [`FnCtxt::coerce`][fnctxt_coerce]. ## LUB coercions -With a LUB coercion we coerce a set of source types to some unknown target type. +With a LUB coercion, we coerce a set of source types to some unknown target type. Unlike one-to-one coercions, a LUB coercion *produces* the target type that all of the source types coerce to. -In the above example this would be the LUB coercion of both `&mut i32` and `&i32`, where we produce the target type `&i32`. +In the above example this would be the LUB coercion of both `&mut i32` and `&i32`, +where we produce the target type `&i32`. The name "LUB coercion" (Least-Upper-Bound coercion) comes from how this coercion takes a set of types and computes the least coerced/subtyped type that both source types are coercable/subtypeable into. @@ -55,11 +56,14 @@ There are a few key steps here: ### Step 1 -First we create a [`CoerceMany`][coerce_many] value, this stores all of the state required for the LUB coercion. -Unlike one-to-one coercions, a LUB coercion isn't a single function call as we want to intermix typechecking with advancing the LUB coercion. +First we create a [`CoerceMany`][coerce_many] value. +This stores all of the state required for the LUB coercion. +Unlike one-to-one coercions, +a LUB coercion isn't a single function call as we want to intermix typechecking with advancing the LUB coercion. Creating a `CoerceMany` takes some `initial_lub` type. -This is different from the *target* of the coercion which is an output of a LUB coercion rather than an input (unlike a one-to-one coercion). +This is different from the *target* of the coercion, +which is an output of a LUB coercion rather than an input (unlike a one-to-one coercion). The initial lub ty should be derived from the [`Expectation`][expectation] for whatever expression this LUB coercion is for. It allows for inference constraints from computing the LUB coercion to propagate into the `Expectation`s used for type checking later expressions participating in the LUB coercion. @@ -72,8 +76,9 @@ If there's no `Expectation` to use then some new infer var should be made for th Next, for each expression participating in the LUB coercion, we typecheck it then invoke [`CoerceMany::coerce`][coerce_many_coerce] with its type. -In some cases the expression participating in the LUB coercion doesn't actually exist in the HIR. -For example when handling an operand-less `break` or `return` expression we need `()` to participate in the LUB coercion. +In some cases, the expression participating in the LUB coercion doesn't actually exist in the HIR. +For example, when handling an operand-less `break` or `return` expression, +we need `()` to participate in the LUB coercion. In these cases the [`CoerceMany::coerce_forced_unit`][coerce_many_coerce_forced_unit] method can be used. @@ -93,14 +98,17 @@ You should always take the resulting type of the LUB coercion and perform any ne When a coerce operation succeeds we record what kind of coercion it was, for example an unsize coercion or an autoderef etc. This is handled as part of the coerce operation by writing a list of *adjustments* into the in-progress [`TypeckResults`][typeck_results]. -When building THIR we take the adjustments stored in the `TypeckResults` and make all of the coercion steps explicit. -After this point in the compiler there isn't really a notion of coercions, only explicit casts and subtyping in the MIR. +When building THIR, +we take the adjustments stored in the `TypeckResults` and make all of the coercion steps explicit. +After this point in the compiler, +there isn't really a notion of coercions, only explicit casts and subtyping in the MIR. TODO: write and link to an adjustments chapter here ### How does `CoerceMany` work -[`CoerceMany`][coerce_many] works by repeatedly taking the current lub ty and some new source type, and computing a new lub ty which both types can coerce to. +[`CoerceMany`][coerce_many] works by repeatedly taking the current lub ty and some new source type, +and computing a new lub ty which both types can coerce to. The core logic of taking a pair of types and computing some new third type can be found in [`try_find_coercion_lub`][try_find_coercion_lub]. ```rust @@ -114,14 +122,17 @@ let a = match my_bool { } ``` -In this example when type checking the `match` expression a LUB coercion is performed. +In this example, when type checking the `match` expression, a LUB coercion is performed. This LUB coercion starts out with an initial lub ty of some inference variable `?x` due to the let statement having no known type. There are three expressions that participate in this LUB coercion. -The first expression of a LUB coercion is special, instead of computing a new type with the existing initial lub ty, we coerce directly from the first expression to the initial lub ty. +The first expression of a LUB coercion is special; +instead of computing a new type with the existing initial lub ty, +we coerce directly from the first expression to the initial lub ty. 1. After type checking `true => foo,` we wind up with the type `FnDef(Foo)`. - We then call [`CoerceMany::coerce`][coerce_many_coerce] which will perform a one-to-one coercion of `FnDef(Foo)` to `?x`. + We then call [`CoerceMany::coerce`][coerce_many_coerce], + which will perform a one-to-one coercion of `FnDef(Foo)` to `?x`. This infers `?x=FnDef(Foo)` giving us a new lub ty for the LUB coercion. 2. After type checking `true if other_bool => foo,` we once again wind up with the type `FnDef(Foo)`. We'll then call `CoerceMany::coerce` which will attempt to compute a new lub ty from our previous lub ty (`FnDef(Foo)`) and the type of this expression (`FnDef(Foo)`). @@ -144,7 +155,7 @@ use std::ops::Deref; impl Deref for Foo { type Target = [u8; 2]; - + fn deref(&self) -> &[u8; 2] { &[1; _] } @@ -175,7 +186,9 @@ The current implementation of transitive coercions is broken, the previous examp While the logic for performing a LUB coercion can produce transitive coercions just fine, the rest of the compiler is not set up to handle them. One-to-one coercions are also not capable of producing a lot of the kinds of transitive coercions that LUB coercions can. -For example if we take the previous example and turn it into a one-to-one coercion we get a compile error: +For example, if we take the previous example and turn it into a one-to-one coercion, +we get a compile error: + ```rust struct Foo; @@ -183,7 +196,7 @@ use std::ops::Deref; impl Deref for Foo { type Target = [u8; 2]; - + fn deref(&self) -> &[u8; 2] { &[1; _] } @@ -215,18 +228,23 @@ There is likely room for improving the structure of this function to make it mor The implementation of one-to-one coercions is reused as part of LUB coercions. -It would be wrong for LUB coercions to use one way subtyping when relating signatures or falling back to subtyping in the case of no coercions being possible. -Instead we want to compute a mutual supertype of the two types. +It would be wrong for LUB coercions to use one way subtyping when relating signatures, +or falling back to subtyping in the case of no coercions being possible. +Instead, we want to compute a mutual supertype of the two types. -The `use_lub` field on [`Coerce`][coerce_ty] exists to toggle whether to perform normal subtyping (in the case of a one-to-one coercion), or whether to compute a mutual supertype (in the case of a LUB coercion). +The `use_lub` field on [`Coerce`][coerce_ty] exists to toggle whether to perform normal subtyping (in the case of a one-to-one coercion), +or whether to compute a mutual supertype (in the case of a LUB coercion). ### Lubbing -In theory computing a mutual supertype should be as simple as creating some new infer var `?mutual_sup` and then requiring `lub_ty <: ?mutual_sup` and `new_ty <: ?mutual_sup`. -In reality LUB coercions use a special [`TypeRelation`][type_relation], [`LatticeOp`][lattice_op]. +In theory, +computing a mutual supertype should be as simple as creating some new infer var `?mutual_sup`, +and then requiring `lub_ty <: ?mutual_sup` and `new_ty <: ?mutual_sup`. +In reality, LUB coercions use a special [`TypeRelation`][type_relation], [`LatticeOp`][lattice_op]. This is primarily to work around subtyping/generalization for higher ranked types being fairly broken. -Unlike normal subtyping, when encountering higher ranked types the lub type relation will switch to invariance. +Unlike normal subtyping, when encountering higher ranked types, +the lub type relation will switch to invariance. This enforces that the binders of the higher ranked types are equivalent which avoids the need to pick a "most general" binder, which would be quite difficult to do. @@ -246,7 +264,8 @@ Care should be taken when coercing from inside of a probe as both one-to-one coe LUB coercions will emit error when a coercion step fails, this makes it entirely suitable for use inside of probes. 1-to-1 and LUB coercions will both apply *adjustments* to the coerced expressions on success. -This means that if inside of a probe and an attempt to coerce succeeds, then the probe must not rollback anything. +This means that if inside of a probe and an attempt to coerce succeeds, +then the probe must not rollback anything. It's therefore correct to wrap a [`FnCtxt::coerce`][fnctxt_coerce] call inside of a [`commit_if_ok`][commit_if_ok], but would be wrong to do so if returning `Err` after the coerce call. It would also be wrong to call `FnCtxt::coerce` from within a [`probe`][probe]. @@ -297,10 +316,13 @@ If we use `?x` as the initial lub ty of the LUB coercion then we would get the f - expression 1: infer `?x=FnDef(a)` - expression 2: find a coercion lub between `FnDef(a), FnDef(b)` resulting in `fn() -> ()` - the final type of the LUB coercion is `fn() -> ()`. - equate `?x eq fn() -> ()`, where `?x` actually already has been inferred to `FnDef(a)`, so this is actually equating `FnDef(a) eq fn() -> ()` which does not hold + equate `?x eq fn() -> ()`, where `?x` actually already has been inferred to `FnDef(a)`, + so this is actually equating `FnDef(a) eq fn() -> ()` which does not hold -To avoid some (but not all) of these undesirable inference constraints, if the `Expectation` for the LUB coercion is an inference variable then we won't use it as the initial lub ty. -Instead we create a new infer var, for example in the above code snippet we would actually make some new infer var `?y` for the initial lub ty instead of using `?x`. +To avoid some (but not all) of these undesirable inference constraints, +if the `Expectation` for the LUB coercion is an inference variable then we won't use it as the initial lub ty. +Instead we create a new infer var, for example in the above code snippet, +we would actually make some new infer var `?y` for the initial lub ty instead of using `?x`. - expression 1: infer `?y=FnDef(a)` - expression 2: find a coercion lub between `FnDef(a), FnDef(b)` resulting in `fn() -> ()` - the final type of the LUB coercion is `fn() -> ()`, infer `?x=fn() -> ()` From 882691384d14f8bde73694b99bb5dd1a859776c8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:06:23 +0200 Subject: [PATCH 11/22] sembr src/hir-typeck/method-lookup.md --- src/hir-typeck/method-lookup.md | 89 ++++++++++++++++----------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/src/hir-typeck/method-lookup.md b/src/hir-typeck/method-lookup.md index c8d529a32b..4cc215555c 100644 --- a/src/hir-typeck/method-lookup.md +++ b/src/hir-typeck/method-lookup.md @@ -2,8 +2,8 @@ Method lookup can be rather complex due to the interaction of a number of factors, such as self types, autoderef, trait lookup, etc. This -file provides an overview of the process. More detailed notes are in -the code itself, naturally. +file provides an overview of the process. +More detailed notes are in the code itself, naturally. One way to think of method lookup is that we convert an expression of the form `receiver.method(...)` into a more explicit [fully-qualified syntax][] @@ -19,16 +19,17 @@ particular unsizing (e.g., converting from `[T; n]` to `[T]`). Method lookup is divided into two major phases: -1. Probing ([`probe.rs`][probe]). The probe phase is when we decide what method - to call and how to adjust the receiver. -2. Confirmation ([`confirm.rs`][confirm]). The confirmation phase "applies" +1. Probing ([`probe.rs`][probe]). + The probe phase is when we decide what method to call and how to adjust the receiver. +2. Confirmation ([`confirm.rs`][confirm]). + The confirmation phase "applies" this selection, updating the side-tables, unifying type variables, and otherwise doing side-effectful things. -One reason for this division is to be more amenable to caching. The -probe phase produces a "pick" (`probe::Pick`), which is designed to be -cacheable across method-call sites. Therefore, it does not include -inference variables or other information. +One reason for this division is to be more amenable to caching. + The probe phase produces a "pick" (`probe::Pick`), which is designed to be +cacheable across method-call sites. +Therefore, it does not include inference variables or other information. [fully-qualified syntax]: https://doc.rust-lang.org/nightly/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name [UFCS]: https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md @@ -41,9 +42,8 @@ inference variables or other information. The first thing that the probe phase does is to create a series of *steps*. This is done by progressively dereferencing the receiver type -until it cannot be deref'd anymore, as well as applying an optional -"unsize" step. So if the receiver has type `Rc>`, this -might yield: +until it cannot be deref'd anymore, as well as applying an optional "unsize" step. +So if the receiver has type `Rc>`, this might yield: 1. `Rc>` 2. `Box<[T; 3]>` @@ -53,26 +53,24 @@ might yield: ### Candidate assembly We then search along those steps to create a list of *candidates*. A -`Candidate` is a method item that might plausibly be the method being -invoked. For each candidate, we'll derive a "transformed self type" -that takes into account explicit self. +`Candidate` is a method item that might plausibly be the method being invoked. +For each candidate, we'll derive a "transformed self type" that takes into account explicit self. Candidates are grouped into two kinds, inherent and extension. -**Inherent candidates** are those that are derived from the -type of the receiver itself. So, if you have a receiver of some +**Inherent candidates** are those that are derived from the type of the receiver itself. + So, if you have a receiver of some nominal type `Foo` (e.g., a struct), any methods defined within an -impl like `impl Foo` are inherent methods. Nothing needs to be -imported to use an inherent method, they are associated with the type -itself (note that inherent impls can only be defined in the same -crate as the type itself). +impl like `impl Foo` are inherent methods. + Nothing needs to be imported to use an inherent method, they are associated with the type +itself (note that inherent impls can only be defined in the same crate as the type itself). -**Extension candidates** are derived from imported traits. If I have -the trait `ToString` imported, and I call `to_string()` as a method, -then we will list the `to_string()` definition in each impl of -`ToString` as a candidate. These kinds of method calls are called -"extension methods". +**Extension candidates** are derived from imported traits. + If I have the trait `ToString` imported, and I call `to_string()` as a method, +then we will list the `to_string()` definition in each impl of `ToString` as a candidate. +These kinds of method calls are called "extension methods". -So, let's continue our example. Imagine that we were calling a method +So, let's continue our example. +Imagine that we were calling a method `foo` with the receiver `Rc>` and there is a trait `Foo` that defines it with `&self` for the type `Rc` as well as a method -on the type `Box` that defines `foo` but with `&mut self`. Then we -might have two candidates: +on the type `Box` that defines `foo` but with `&mut self`. +Then we might have two candidates: - `&Rc` as an extension candidate - `&mut Box` as an inherent candidate @@ -98,18 +96,17 @@ might have two candidates: ### Candidate search Finally, to actually pick the method, we will search down the steps, -trying to match the receiver type against the candidate types. At -each step, we also consider an auto-ref and auto-mut-ref to see whether -that makes any of the candidates match. For each resulting receiver -type, we consider inherent candidates before extension candidates. +trying to match the receiver type against the candidate types. +At each step, we also consider an auto-ref and auto-mut-ref to see whether +that makes any of the candidates match. +For each resulting receiver type, we consider inherent candidates before extension candidates. If there are multiple matching candidates in a group, we report an -error, except that multiple impls of the same trait are treated as a -single match. Otherwise we pick the first match we find. +error, except that multiple impls of the same trait are treated as a single match. +Otherwise we pick the first match we find. In the case of our example, the first step is `Rc>`, -which does not itself match any candidate. But when we autoref it, we -get the type `&Rc>` which matches `&Rc`. We would then -recursively consider all where-clauses that appear on the impl: if -those match (or we cannot rule out that they do), then this is the -method we would pick. Otherwise, we would continue down the series of -steps. +which does not itself match any candidate. +But when we autoref it, we get the type `&Rc>` which matches `&Rc`. +We would then recursively consider all where-clauses that appear on the impl: if +those match (or we cannot rule out that they do), then this is the method we would pick. +Otherwise, we would continue down the series of steps. From 5e287f41638dfd62f209d390dab7a20c44b1d95e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:14:09 +0200 Subject: [PATCH 12/22] improve hir-typeck/method-lookup.md --- src/hir-typeck/method-lookup.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/hir-typeck/method-lookup.md b/src/hir-typeck/method-lookup.md index 4cc215555c..6204f7f24e 100644 --- a/src/hir-typeck/method-lookup.md +++ b/src/hir-typeck/method-lookup.md @@ -1,8 +1,8 @@ # Method lookup Method lookup can be rather complex due to the interaction of a number -of factors, such as self types, autoderef, trait lookup, etc. This -file provides an overview of the process. +of factors, such as self types, autoderef, trait lookup, etc. +This file provides an overview of the process. More detailed notes are in the code itself, naturally. One way to think of method lookup is that we convert an expression of @@ -22,13 +22,12 @@ Method lookup is divided into two major phases: 1. Probing ([`probe.rs`][probe]). The probe phase is when we decide what method to call and how to adjust the receiver. 2. Confirmation ([`confirm.rs`][confirm]). - The confirmation phase "applies" - this selection, updating the side-tables, unifying type variables, and - otherwise doing side-effectful things. + The confirmation phase "applies" this selection, updating the side-tables, + unifying type variables, and otherwise doing side-effectful things. One reason for this division is to be more amenable to caching. - The probe phase produces a "pick" (`probe::Pick`), which is designed to be -cacheable across method-call sites. +The probe phase produces a "pick" (`probe::Pick`), +which is designed to be cacheable across method-call sites. Therefore, it does not include inference variables or other information. [fully-qualified syntax]: https://doc.rust-lang.org/nightly/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name @@ -59,15 +58,15 @@ For each candidate, we'll derive a "transformed self type" that takes into accou Candidates are grouped into two kinds, inherent and extension. **Inherent candidates** are those that are derived from the type of the receiver itself. - So, if you have a receiver of some -nominal type `Foo` (e.g., a struct), any methods defined within an -impl like `impl Foo` are inherent methods. - Nothing needs to be imported to use an inherent method, they are associated with the type -itself (note that inherent impls can only be defined in the same crate as the type itself). +So, if you have a receiver of some nominal type `Foo` (e.g., a struct), +any methods defined within an impl like `impl Foo` are inherent methods. +Nothing needs to be imported to use an inherent method; +they are associated with the type itself. +Note that inherent impls can only be defined in the same crate as the type itself. **Extension candidates** are derived from imported traits. - If I have the trait `ToString` imported, and I call `to_string()` as a method, +If I have the trait `ToString` imported, and I call `to_string()` as a method, then we will list the `to_string()` definition in each impl of `ToString` as a candidate. These kinds of method calls are called "extension methods". From 49bcc86b9ffac92c1ddf5b4cec34bf408313e4f4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:15:27 +0200 Subject: [PATCH 13/22] sembr src/backend/lowering-mir.md --- src/backend/lowering-mir.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/backend/lowering-mir.md b/src/backend/lowering-mir.md index 8b9dbe7ce2..47dfe4e95c 100644 --- a/src/backend/lowering-mir.md +++ b/src/backend/lowering-mir.md @@ -1,14 +1,14 @@ # Lowering MIR to a Codegen IR Now that we have a list of symbols to generate from the collector, we need to -generate some sort of codegen IR. In this chapter, we will assume LLVM IR, -since that's what rustc usually uses. The actual monomorphization is performed -as we go, while we do the translation. +generate some sort of codegen IR. +In this chapter, we will assume LLVM IR, +since that's what rustc usually uses. +The actual monomorphization is performed as we go, while we do the translation. -Recall that the backend is started by -[`rustc_codegen_ssa::base::codegen_crate`][codegen1]. Eventually, this reaches -[`rustc_codegen_ssa::mir::codegen_mir`][codegen2], which does the lowering from -MIR to LLVM IR. +Recall that the backend is started by [`rustc_codegen_ssa::base::codegen_crate`][codegen1]. +Eventually, this reaches +[`rustc_codegen_ssa::mir::codegen_mir`][codegen2], which does the lowering from MIR to LLVM IR. [codegen1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_crate.html [codegen2]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/fn.codegen_mir.html @@ -16,7 +16,8 @@ MIR to LLVM IR. The code is split into modules which handle particular MIR primitives: - [`rustc_codegen_ssa::mir::block`][mirblk] will deal with translating - blocks and their terminators. The most complicated and also the most + blocks and their terminators. + The most complicated and also the most interesting thing this module does is generating code for function calls, including the necessary unwinding handling IR. - [`rustc_codegen_ssa::mir::statement`][mirst] translates MIR statements. @@ -31,24 +32,26 @@ The code is split into modules which handle particular MIR primitives: [mirrv]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/rvalue/index.html Before a function is translated a number of simple and primitive analysis -passes will run to help us generate simpler and more efficient LLVM IR. An -example of such an analysis pass would be figuring out which variables are +passes will run to help us generate simpler and more efficient LLVM IR. +An example of such an analysis pass would be figuring out which variables are SSA-like, so that we can translate them to SSA directly rather than relying on -LLVM's `mem2reg` for those variables. The analysis can be found in -[`rustc_codegen_ssa::mir::analyze`][mirana]. +LLVM's `mem2reg` for those variables. +The analysis can be found in [`rustc_codegen_ssa::mir::analyze`][mirana]. [mirana]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/analyze/index.html Usually a single MIR basic block will map to a LLVM basic block, with very few exceptions: intrinsic or function calls and less basic MIR statements like -`assert` can result in multiple basic blocks. This is a perfect lede into the -non-portable LLVM-specific part of the code generation. Intrinsic generation is +`assert` can result in multiple basic blocks. +This is a perfect lede into the non-portable LLVM-specific part of the code generation. +Intrinsic generation is fairly easy to understand as it involves very few abstraction levels in between and can be found in [`rustc_codegen_llvm::intrinsic`][llvmint]. [llvmint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/intrinsic/index.html -Everything else will use the [builder interface][builder]. This is the code that gets +Everything else will use the [builder interface][builder]. +This is the code that gets called in the [`rustc_codegen_ssa::mir::*`][ssamir] modules discussed above. [builder]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/builder/index.html From bf680e4df10e15c3aeac627257516b89b52678eb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:19:59 +0200 Subject: [PATCH 14/22] reflow --- src/backend/lowering-mir.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backend/lowering-mir.md b/src/backend/lowering-mir.md index 47dfe4e95c..ade8a411ec 100644 --- a/src/backend/lowering-mir.md +++ b/src/backend/lowering-mir.md @@ -17,8 +17,8 @@ The code is split into modules which handle particular MIR primitives: - [`rustc_codegen_ssa::mir::block`][mirblk] will deal with translating blocks and their terminators. - The most complicated and also the most - interesting thing this module does is generating code for function calls, + The most complicated and also the most interesting thing this module does + is generating code for function calls, including the necessary unwinding handling IR. - [`rustc_codegen_ssa::mir::statement`][mirst] translates MIR statements. - [`rustc_codegen_ssa::mir::operand`][mirop] translates MIR operands. @@ -44,15 +44,15 @@ Usually a single MIR basic block will map to a LLVM basic block, with very few exceptions: intrinsic or function calls and less basic MIR statements like `assert` can result in multiple basic blocks. This is a perfect lede into the non-portable LLVM-specific part of the code generation. -Intrinsic generation is -fairly easy to understand as it involves very few abstraction levels in between +Intrinsic generation is fairly easy to understand +as it involves very few abstraction levels in between and can be found in [`rustc_codegen_llvm::intrinsic`][llvmint]. [llvmint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/intrinsic/index.html Everything else will use the [builder interface][builder]. -This is the code that gets -called in the [`rustc_codegen_ssa::mir::*`][ssamir] modules discussed above. +This is the code that gets called in the +[`rustc_codegen_ssa::mir::*`][ssamir] modules discussed above. [builder]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/builder/index.html [ssamir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/index.html From e199f7f8dd079313eefd291cb46f8f436f3dd9e8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:21:19 +0200 Subject: [PATCH 15/22] sembr src/backend/implicit-caller-location.md --- src/backend/implicit-caller-location.md | 70 +++++++++++++++---------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/backend/implicit-caller-location.md b/src/backend/implicit-caller-location.md index 9ca4bcab07..2731e29691 100644 --- a/src/backend/implicit-caller-location.md +++ b/src/backend/implicit-caller-location.md @@ -1,8 +1,8 @@ # Implicit caller location Approved in [RFC 2091], this feature enables the accurate reporting of caller location during panics -initiated from functions like `Option::unwrap`, `Result::expect`, and `Index::index`. This feature -adds the [`#[track_caller]`][attr-reference] attribute for functions, the +initiated from functions like `Option::unwrap`, `Result::expect`, and `Index::index`. +This feature adds the [`#[track_caller]`][attr-reference] attribute for functions, the [`caller_location`][intrinsic] intrinsic, and the stabilization-friendly [`core::panic::Location::caller`][wrapper] wrapper. @@ -40,14 +40,16 @@ library which propagate caller information. ## Reading caller location Previously, `panic!` made use of the `file!()`, `line!()`, and `column!()` macros to construct a -[`Location`] pointing to where the panic occurred. These macros couldn't be given an overridden +[`Location`] pointing to where the panic occurred. +These macros couldn't be given an overridden location, so functions which intentionally invoked `panic!` couldn't provide their own location, hiding the actual source of error. Internally, `panic!()` now calls [`core::panic::Location::caller()`][wrapper] to find out where it -was expanded. This function is itself annotated with `#[track_caller]` and wraps the -[`caller_location`][intrinsic] compiler intrinsic implemented by rustc. This intrinsic is easiest -explained in terms of how it works in a `const` context. +was expanded. +This function is itself annotated with `#[track_caller]` and wraps the +[`caller_location`][intrinsic] compiler intrinsic implemented by rustc. +This intrinsic is easiest explained in terms of how it works in a `const` context. ## Caller location in `const` @@ -57,21 +59,23 @@ to find the right location and allocating a const value to return. ### Finding the right `Location` In a const context we "walk up the stack" from where the intrinsic is invoked, stopping when we -reach the first function call in the stack which does *not* have the attribute. This walk is in -[`InterpCx::find_closest_untracked_caller_location()`][const-find-closest]. +reach the first function call in the stack which does *not* have the attribute. +This walk is in [`InterpCx::find_closest_untracked_caller_location()`][const-find-closest]. Starting at the bottom, we iterate up over stack [`Frame`][const-frame]s in the [`InterpCx::stack`][const-stack], calling [`InstanceKind::requires_caller_location`][requires-location] on the -[`Instance`s from each `Frame`][frame-instance]. We stop once we find one that returns `false` and +[`Instance`s from each `Frame`][frame-instance]. +We stop once we find one that returns `false` and return the span of the *previous* frame which was the "topmost" tracked function. ### Allocating a static `Location` Once we have a `Span`, we need to allocate static memory for the `Location`, which is performed by -the [`TyCtxt::const_caller_location()`][const-location-query] query. Internally this calls -[`InterpCx::alloc_caller_location()`][alloc-location] and results in a unique -[memory kind][location-memory-kind] (`MemoryKind::CallerLocation`). The SSA codegen backend is able +the [`TyCtxt::const_caller_location()`][const-location-query] query. +Internally this calls [`InterpCx::alloc_caller_location()`][alloc-location] and results in a unique +[memory kind][location-memory-kind] (`MemoryKind::CallerLocation`). +The SSA codegen backend is able to emit code for these same values, and we use this code there as well. Once our `Location` has been allocated in static memory, our intrinsic returns a reference to it. @@ -79,13 +83,14 @@ Once our `Location` has been allocated in static memory, our intrinsic returns a ## Generating code for `#[track_caller]` callees To generate efficient code for a tracked function and its callers, we need to provide the same -behavior from the intrinsic's point of view without having a stack to walk up at runtime. We invert +behavior from the intrinsic's point of view without having a stack to walk up at runtime. +We invert the approach: as we grow the stack down we pass an additional argument to calls of tracked functions -rather than walking up the stack when the intrinsic is called. That additional argument can be -returned wherever the caller location is queried. +rather than walking up the stack when the intrinsic is called. +That additional argument can be returned wherever the caller location is queried. -The argument we append is of type `&'static core::panic::Location<'static>`. A reference was chosen -to avoid unnecessary copying because a pointer is a third the size of +The argument we append is of type `&'static core::panic::Location<'static>`. +A reference was chosen to avoid unnecessary copying because a pointer is a third the size of `std::mem::size_of::() == 24` at time of writing. When generating a call to a function which is tracked, we pass the location argument the value of @@ -105,7 +110,8 @@ stack downward. ### Codegen examples -What does this transformation look like in practice? Take this example which uses the new feature: +What does this transformation look like in practice? +Take this example which uses the new feature: ```rust #![feature(track_caller)] @@ -139,13 +145,15 @@ fn main() { ### Dynamic dispatch In codegen contexts we have to modify the callee ABI to pass this information down the stack, but -the attribute expressly does *not* modify the type of the function. The ABI change must be -transparent to type checking and remain sound in all uses. +the attribute expressly does *not* modify the type of the function. +The ABI change must be transparent to type checking and remain sound in all uses. Direct calls to tracked functions will always know the full codegen flags for the callee and can -generate appropriate code. Indirect callers won't have this information and it's not encoded in +generate appropriate code. +Indirect callers won't have this information and it's not encoded in the type of the function pointer they call, so we generate a [`ReifyShim`] around the function -whenever taking a pointer to it. This shim isn't able to report the actual location of the indirect +whenever taking a pointer to it. +This shim isn't able to report the actual location of the indirect call (the function's definition site is reported instead), but it prevents miscompilation and is probably the best we can do without modifying fully-stabilized type signatures. @@ -163,7 +171,8 @@ function: * is not a closure * is not `#[naked]` -If the use is valid, we set [`CodegenFnAttrsFlags::TRACK_CALLER`][attrs-flags]. This flag influences +If the use is valid, we set [`CodegenFnAttrsFlags::TRACK_CALLER`][attrs-flags]. +This flag influences the return value of [`InstanceKind::requires_caller_location`][requires-location] which is in turn used in both const and codegen contexts to ensure correct propagation. @@ -172,7 +181,8 @@ used in both const and codegen contexts to ensure correct propagation. When applied to trait method implementations, the attribute works as it does for regular functions. When applied to a trait method prototype, the attribute applies to all implementations of the -method. When applied to a default trait method implementation, the attribute takes effect on +method. +When applied to a default trait method implementation, the attribute takes effect on that implementation *and* any overrides. Examples: @@ -242,19 +252,23 @@ stability guarantees, requiring modifications to end-user source, relying on pla debug-info, or preventing user-defined types from having the same error-reporting benefits. Improving the output of these panics has been a goal of proposals since at least mid-2016 (see -[non-viable alternatives] in the approved RFC for details). It took two more years until RFC 2091 +[non-viable alternatives] in the approved RFC for details). +It took two more years until RFC 2091 was approved, much of its [rationale] for this feature's design having been discovered through the discussion around several earlier proposals. The design in the original RFC limited itself to implementations that could be done inside the -compiler at the time without significant refactoring. However in the year and a half between the +compiler at the time without significant refactoring. +However in the year and a half between the approval of the RFC and the actual implementation work, a [revised design] was proposed and written -up on the tracking issue. During the course of implementing that, it was also discovered that an +up on the tracking issue. +During the course of implementing that, it was also discovered that an implementation was possible without modifying the number of arguments in a function's MIR, which would simplify later stages and unlock use in traits. Because the RFC's implementation strategy could not readily support traits, the semantics were not -originally specified. They have since been implemented following the path which seemed most correct +originally specified. +They have since been implemented following the path which seemed most correct to the author and reviewers. [RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md From 9dd7005fe39cbde881df81115cf58303285bf804 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:26:41 +0200 Subject: [PATCH 16/22] reflow --- src/backend/implicit-caller-location.md | 31 ++++++++++++------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/backend/implicit-caller-location.md b/src/backend/implicit-caller-location.md index 2731e29691..909c00ea40 100644 --- a/src/backend/implicit-caller-location.md +++ b/src/backend/implicit-caller-location.md @@ -41,8 +41,8 @@ library which propagate caller information. Previously, `panic!` made use of the `file!()`, `line!()`, and `column!()` macros to construct a [`Location`] pointing to where the panic occurred. -These macros couldn't be given an overridden -location, so functions which intentionally invoked `panic!` couldn't provide their own location, +These macros couldn't be given an overridden location, +so functions which intentionally invoked `panic!` couldn't provide their own location, hiding the actual source of error. Internally, `panic!()` now calls [`core::panic::Location::caller()`][wrapper] to find out where it @@ -75,8 +75,8 @@ Once we have a `Span`, we need to allocate static memory for the `Location`, whi the [`TyCtxt::const_caller_location()`][const-location-query] query. Internally this calls [`InterpCx::alloc_caller_location()`][alloc-location] and results in a unique [memory kind][location-memory-kind] (`MemoryKind::CallerLocation`). -The SSA codegen backend is able -to emit code for these same values, and we use this code there as well. +The SSA codegen backend is able to emit code for these same values, +and we use this code there as well. Once our `Location` has been allocated in static memory, our intrinsic returns a reference to it. @@ -84,8 +84,8 @@ Once our `Location` has been allocated in static memory, our intrinsic returns a To generate efficient code for a tracked function and its callers, we need to provide the same behavior from the intrinsic's point of view without having a stack to walk up at runtime. -We invert -the approach: as we grow the stack down we pass an additional argument to calls of tracked functions +We invert the approach: +as we grow the stack down we pass an additional argument to calls of tracked functions rather than walking up the stack when the intrinsic is called. That additional argument can be returned wherever the caller location is queried. @@ -172,16 +172,16 @@ function: * is not `#[naked]` If the use is valid, we set [`CodegenFnAttrsFlags::TRACK_CALLER`][attrs-flags]. -This flag influences -the return value of [`InstanceKind::requires_caller_location`][requires-location] which is in turn +This flag influences the return value of +[`InstanceKind::requires_caller_location`][requires-location] which is in turn used in both const and codegen contexts to ensure correct propagation. ### Traits When applied to trait method implementations, the attribute works as it does for regular functions. -When applied to a trait method prototype, the attribute applies to all implementations of the -method. +When applied to a trait method prototype, +the attribute applies to all implementations of the method. When applied to a default trait method implementation, the attribute takes effect on that implementation *and* any overrides. @@ -245,7 +245,7 @@ fn main() { } ``` -## Background/History +## Background/history Broadly speaking, this feature's goal is to improve common Rust error messages without breaking stability guarantees, requiring modifications to end-user source, relying on platform-specific @@ -253,15 +253,14 @@ debug-info, or preventing user-defined types from having the same error-reportin Improving the output of these panics has been a goal of proposals since at least mid-2016 (see [non-viable alternatives] in the approved RFC for details). -It took two more years until RFC 2091 -was approved, much of its [rationale] for this feature's design having been discovered through the +It took two more years until RFC 2091 was approved, +much of its [rationale] for this feature's design having been discovered through the discussion around several earlier proposals. The design in the original RFC limited itself to implementations that could be done inside the compiler at the time without significant refactoring. -However in the year and a half between the -approval of the RFC and the actual implementation work, a [revised design] was proposed and written -up on the tracking issue. +However in the year and a half between the approval of the RFC and the actual implementation work, +a [revised design] was proposed and written up on the tracking issue. During the course of implementing that, it was also discovered that an implementation was possible without modifying the number of arguments in a function's MIR, which would simplify later stages and unlock use in traits. From 535edfb8505806e8e1314cfb83b1625aa73db3a9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:27:51 +0200 Subject: [PATCH 17/22] sembr src/backend/codegen.md --- src/backend/codegen.md | 62 ++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/backend/codegen.md b/src/backend/codegen.md index e2c92430e6..1668e8a043 100644 --- a/src/backend/codegen.md +++ b/src/backend/codegen.md @@ -6,8 +6,7 @@ Usually, rustc uses LLVM for code generation, but there is also support for [Cranelift] and [GCC]. The key is that rustc doesn't implement codegen itself. It's worth noting, though, that in the Rust source code, -many parts of the backend have `codegen` in their names -(there are no hard boundaries). +many parts of the backend have `codegen` in their names (there are no hard boundaries). [Cranelift]: https://github.com/bytecodealliance/wasmtime/tree/main/cranelift [GCC]: https://github.com/rust-lang/rustc_codegen_gcc @@ -20,28 +19,32 @@ many parts of the backend have `codegen` in their names ## What is LLVM? [LLVM](https://llvm.org) is "a collection of modular and reusable compiler and -toolchain technologies". In particular, the LLVM project contains a pluggable +toolchain technologies". +In particular, the LLVM project contains a pluggable compiler backend (also called "LLVM"), which is used by many compiler projects, including the `clang` C compiler and our beloved `rustc`. -LLVM takes input in the form of LLVM IR. It is basically assembly code with -additional low-level types and annotations added. These annotations are helpful -for doing optimizations on the LLVM IR and outputted machine code. The end +LLVM takes input in the form of LLVM IR. +It is basically assembly code with additional low-level types and annotations added. +These annotations are helpful for doing optimizations on the LLVM IR and outputted machine code. +The end result of all this is (at long last) something executable (e.g. an ELF object, an EXE, or wasm). There are a few benefits to using LLVM: -- We don't have to write a whole compiler backend. This reduces implementation - and maintenance burden. +- We don't have to write a whole compiler backend. + This reduces implementation and maintenance burden. - We benefit from the large suite of advanced optimizations that the LLVM project has been collecting. -- We can automatically compile Rust to any of the platforms for which LLVM has - support. For example, as soon as LLVM added support for wasm, voila! rustc, - clang, and a bunch of other languages were able to compile to wasm! (Well, +- We can automatically compile Rust to any of the platforms for which LLVM has support. + For example, as soon as LLVM added support for wasm, voila! + rustc, + clang, and a bunch of other languages were able to compile to wasm! + (Well, there was some extra stuff to be done, but we were 90% there anyway). -- We and other compiler projects benefit from each other. For example, when the - [Spectre and Meltdown security vulnerabilities][spectre] were discovered, +- We and other compiler projects benefit from each other. + For example, when the [Spectre and Meltdown security vulnerabilities][spectre] were discovered, only LLVM needed to be patched. [spectre]: https://meltdownattack.com/ @@ -49,25 +52,24 @@ There are a few benefits to using LLVM: ## Running LLVM, linking, and metadata generation Once LLVM IR for all of the functions and statics, etc is built, it is time to -start running LLVM and its optimization passes. LLVM IR is grouped into -"modules". Multiple "modules" can be codegened at the same time to aid in -multi-core utilization. These "modules" are what we refer to as _codegen -units_. These units were established way back during monomorphization -collection phase. +start running LLVM and its optimization passes. +LLVM IR is grouped into "modules". +Multiple "modules" can be codegened at the same time to aid in multi-core utilization. +These "modules" are what we refer to as _codegen units_. +These units were established way back during monomorphization collection phase. Once LLVM produces objects from these modules, these objects are passed to the -linker along with, optionally, the metadata object and an archive or an -executable is produced. - -It is not necessarily the codegen phase described above that runs the -optimizations. With certain kinds of LTO, the optimization might happen at the -linking time instead. It is also possible for some optimizations to happen -before objects are passed on to the linker and some to happen during the -linking. - -This all happens towards the very end of compilation. The code for this can be -found in [`rustc_codegen_ssa::back`][ssaback] and -[`rustc_codegen_llvm::back`][llvmback]. Sadly, this piece of code is not +linker along with, optionally, the metadata object and an archive or an executable is produced. + +It is not necessarily the codegen phase described above that runs the optimizations. +With certain kinds of LTO, the optimization might happen at the linking time instead. +It is also possible for some optimizations to happen +before objects are passed on to the linker and some to happen during the linking. + +This all happens towards the very end of compilation. +The code for this can be found in [`rustc_codegen_ssa::back`][ssaback] and +[`rustc_codegen_llvm::back`][llvmback]. +Sadly, this piece of code is not really well-separated into LLVM-dependent code; the [`rustc_codegen_ssa`][ssa] contains a fair amount of code specific to the LLVM backend. From 25b446f3934c65f46ced8febe8030ae5a4af355b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:42:53 +0200 Subject: [PATCH 18/22] improve backend/codegen.md --- src/backend/codegen.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/backend/codegen.md b/src/backend/codegen.md index 1668e8a043..2bf3cd5437 100644 --- a/src/backend/codegen.md +++ b/src/backend/codegen.md @@ -27,8 +27,7 @@ including the `clang` C compiler and our beloved `rustc`. LLVM takes input in the form of LLVM IR. It is basically assembly code with additional low-level types and annotations added. These annotations are helpful for doing optimizations on the LLVM IR and outputted machine code. -The end -result of all this is (at long last) something executable (e.g. an ELF object, +The end result of all this is (at long last) something executable (e.g. an ELF object, an EXE, or wasm). There are a few benefits to using LLVM: @@ -39,10 +38,8 @@ There are a few benefits to using LLVM: project has been collecting. - We can automatically compile Rust to any of the platforms for which LLVM has support. For example, as soon as LLVM added support for wasm, voila! - rustc, - clang, and a bunch of other languages were able to compile to wasm! - (Well, - there was some extra stuff to be done, but we were 90% there anyway). + rustc, clang, and a bunch of other languages were able to compile to wasm! + (Well, there was some extra stuff to be done, but we were 90% there anyway). - We and other compiler projects benefit from each other. For example, when the [Spectre and Meltdown security vulnerabilities][spectre] were discovered, only LLVM needed to be patched. @@ -62,16 +59,15 @@ Once LLVM produces objects from these modules, these objects are passed to the linker along with, optionally, the metadata object and an archive or an executable is produced. It is not necessarily the codegen phase described above that runs the optimizations. -With certain kinds of LTO, the optimization might happen at the linking time instead. +With certain kinds of LTO, the optimization might happen during linking time instead. It is also possible for some optimizations to happen before objects are passed on to the linker and some to happen during the linking. This all happens towards the very end of compilation. The code for this can be found in [`rustc_codegen_ssa::back`][ssaback] and [`rustc_codegen_llvm::back`][llvmback]. -Sadly, this piece of code is not -really well-separated into LLVM-dependent code; the [`rustc_codegen_ssa`][ssa] -contains a fair amount of code specific to the LLVM backend. +Sadly, this piece of code is not really well-separated into LLVM-dependent code; +the [`rustc_codegen_ssa`][ssa] contains a fair amount of code specific to the LLVM backend. Once these components are done with their work you end up with a number of files in your filesystem corresponding to the outputs you have requested. From a1db96964cadb8524a34fabed2dbcdf3cef0558f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:44:40 +0200 Subject: [PATCH 19/22] sembr src/backend/monomorph.md --- src/backend/monomorph.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/backend/monomorph.md b/src/backend/monomorph.md index e9d98597ee..7f6b794d25 100644 --- a/src/backend/monomorph.md +++ b/src/backend/monomorph.md @@ -1,33 +1,33 @@ # Monomorphization As you probably know, Rust has a very expressive type system that has extensive -support for generic types. But of course, assembly is not generic, so we need -to figure out the concrete types of all the generics before the code can -execute. - -Different languages handle this problem differently. For example, in some -languages, such as Java, we may not know the most precise type of value until -runtime. In the case of Java, this is ok because (almost) all variables are +support for generic types. +But of course, assembly is not generic, so we need +to figure out the concrete types of all the generics before the code can execute. + +Different languages handle this problem differently. +For example, in some languages, such as Java, we may not know the most precise type of value until +runtime. +In the case of Java, this is ok because (almost) all variables are reference values anyway (i.e. pointers to a heap allocated object). This flexibility comes at the cost of performance, since all accesses to an object must dereference a pointer. -Rust takes a different approach: it _monomorphizes_ all generic types. This -means that compiler stamps out a different copy of the code of a generic -function for each concrete type needed. For example, if I use a `Vec` and +Rust takes a different approach: it _monomorphizes_ all generic types. +This means that compiler stamps out a different copy of the code of a generic +function for each concrete type needed. +For example, if I use a `Vec` and a `Vec` in my code, then the generated binary will have two copies of the generated code for `Vec`: one for `Vec` and another for `Vec`. The result is fast programs, but it comes at the cost of compile time (creating -all those copies can take a while) and binary size (all those copies might take -a lot of space). +all those copies can take a while) and binary size (all those copies might take a lot of space). Monomorphization is the first step in the backend of the Rust compiler. ## Collection -First, we need to figure out what concrete types we need for all the generic -things in our program. This is called _collection_, and the code that does this -is called the _monomorphization collector_. +First, we need to figure out what concrete types we need for all the generic things in our program. +This is called _collection_, and the code that does this is called the _monomorphization collector_. Take this example: @@ -42,8 +42,9 @@ fn main() { ``` The monomorphization collector will give you a list of `[main, banana, -peach::]`. These are the functions that will have machine code generated -for them. Collector will also add things like statics to that list. +peach::]`. +These are the functions that will have machine code generated for them. +Collector will also add things like statics to that list. See [the collector rustdocs][collect] for more info. @@ -52,8 +53,7 @@ See [the collector rustdocs][collect] for more info. The monomorphization collector is run just before MIR lowering and codegen. [`rustc_codegen_ssa::base::codegen_crate`][codegen1] calls the [`collect_and_partition_mono_items`][mono] query, which does monomorphization -collection and then partitions them into [codegen -units](../appendix/glossary.md#codegen-unit). +collection and then partitions them into [codegen units](../appendix/glossary.md#codegen-unit). ## Codegen Unit (CGU) partitioning From 3e4c13a85f449c270af9d24df083346fdf292ad9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:47:13 +0200 Subject: [PATCH 20/22] reflow --- src/backend/monomorph.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/monomorph.md b/src/backend/monomorph.md index 7f6b794d25..670614fe51 100644 --- a/src/backend/monomorph.md +++ b/src/backend/monomorph.md @@ -6,8 +6,8 @@ But of course, assembly is not generic, so we need to figure out the concrete types of all the generics before the code can execute. Different languages handle this problem differently. -For example, in some languages, such as Java, we may not know the most precise type of value until -runtime. +For example, in some languages, such as Java, +we may not know the most precise type of value until runtime. In the case of Java, this is ok because (almost) all variables are reference values anyway (i.e. pointers to a heap allocated object). This flexibility comes at the cost of performance, since all accesses to an object @@ -16,9 +16,9 @@ must dereference a pointer. Rust takes a different approach: it _monomorphizes_ all generic types. This means that compiler stamps out a different copy of the code of a generic function for each concrete type needed. -For example, if I use a `Vec` and -a `Vec` in my code, then the generated binary will have two copies of -the generated code for `Vec`: one for `Vec` and another for `Vec`. +For example, if I use a `Vec` and a `Vec` in my code, +then the generated binary will have two copies of the generated code for `Vec`: +one for `Vec` and another for `Vec`. The result is fast programs, but it comes at the cost of compile time (creating all those copies can take a while) and binary size (all those copies might take a lot of space). From d9f5e8160040af443c18877693ac363eec7c6d49 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:51:51 +0200 Subject: [PATCH 21/22] fix redirect --- book.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book.toml b/book.toml index 1c9ea7dc01..1ded7aba10 100644 --- a/book.toml +++ b/book.toml @@ -58,7 +58,7 @@ cache-timeout = 90000 warning-policy = "error" [output.html.redirect] -"/backend/inline-asm.html" = "/backend/asm.html" +"/backend/inline-asm.html" = "/asm.html" "/borrow_check.html" = "borrow-check.html" "/borrow_check/drop_check.html" = "/borrow-check/drop-check.html" "/borrow_check/moves_and_initialization.html" = "/borrow-check/moves-and-initialization.html" From 27b69220e3c897e6a01f8490640353a38d5af646 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 19 Aug 2026 08:53:44 +0200 Subject: [PATCH 22/22] sembr src/licenses.md --- src/licenses.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/licenses.md b/src/licenses.md index c4fc59d27c..feb6f7d9a1 100644 --- a/src/licenses.md +++ b/src/licenses.md @@ -25,8 +25,8 @@ that is compatible with Rust’s license. Examples -- Porting C code from a GPL project, like GNU binutils, is not allowed. That would require Rust -itself to be licensed under the GPL. +- Porting C code from a GPL project, like GNU binutils, is not allowed. + That would require Rust itself to be licensed under the GPL. - Copying code from an algorithms text book may be allowed, but some algorithms are patented. ## Porting