From f054c8390b42fbd371c533a51cd4bd22a944af03 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Fri, 4 Sep 2026 07:05:16 +0000 Subject: [PATCH 01/11] Prepare for merging from rust-lang/rust This updates the rust-version file to 71238e21fc55e73ab3aad8c9f79fed7a47a179e1. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 7b170626619e4..386fb6f98c37f 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -da5114692c9ebe46b869488c5f34f92eb10b98c1 +71238e21fc55e73ab3aad8c9f79fed7a47a179e1 From 0271b99df0a328988b4f87c8741271339551b037 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 7 Sep 2026 04:22:37 +0000 Subject: [PATCH 02/11] Prepare for merging from rust-lang/rust This updates the rust-version file to 32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 386fb6f98c37f..18fea436747c7 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -71238e21fc55e73ab3aad8c9f79fed7a47a179e1 +32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba From 41b56aaeb81f3ef9caa214c033f77169b2a50d90 Mon Sep 17 00:00:00 2001 From: Redddy Date: Mon, 7 Sep 2026 17:33:12 +0900 Subject: [PATCH 03/11] Fix reference link name --- src/doc/rustc-dev-guide/src/tests/directives.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 617ad701d129e..5a3ec15d00bf4 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -240,8 +240,8 @@ See also [Debuginfo tests](compiletest.md#debuginfo-tests) for directives for ig [remote testing]: running.md#running-tests-on-a-remote-machine [parallel frontend]: compiletest.md#parallel-frontend [compare modes]: ui.md#compare-modes -[`x86_64-gnu-debug`]: https://github.com/rust-lang/rust/blob/ab3dba92db355b8d97db915a2dca161a117e959c/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile#L32 -[`aarch64-gnu-debug`]: https://github.com/rust-lang/rust/blob/20c909ff9cdd88d33768a4ddb8952927a675b0ad/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile#L32 +[`test-x86_64-gnu-debug`]: https://github.com/rust-lang/rust/blob/ab3dba92db355b8d97db915a2dca161a117e959c/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile#L32 +[`test-aarch64-gnu-debug`]: https://github.com/rust-lang/rust/blob/20c909ff9cdd88d33768a4ddb8952927a675b0ad/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile#L32 ### Affecting how tests are built From 0c5a84b34fccb6d373a1fc89a736b3a78c5df961 Mon Sep 17 00:00:00 2001 From: Redddy Date: Mon, 7 Sep 2026 18:01:02 +0900 Subject: [PATCH 04/11] Update links for test-x86_64-gnu-debug and test-aarch64-gnu-debug --- src/doc/rustc-dev-guide/src/tests/directives.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 5a3ec15d00bf4..22735c124fb43 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -240,8 +240,8 @@ See also [Debuginfo tests](compiletest.md#debuginfo-tests) for directives for ig [remote testing]: running.md#running-tests-on-a-remote-machine [parallel frontend]: compiletest.md#parallel-frontend [compare modes]: ui.md#compare-modes -[`test-x86_64-gnu-debug`]: https://github.com/rust-lang/rust/blob/ab3dba92db355b8d97db915a2dca161a117e959c/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile#L32 -[`test-aarch64-gnu-debug`]: https://github.com/rust-lang/rust/blob/20c909ff9cdd88d33768a4ddb8952927a675b0ad/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile#L32 +[`test-x86_64-gnu-debug`]: https://github.com/rust-lang/rust/blob/32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba/src/ci/docker/host-x86_64/test-x86_64-gnu-debug/Dockerfile#L32 +[`test-aarch64-gnu-debug`]: https://github.com/rust-lang/rust/blob/32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba/src/ci/docker/host-aarch64/test-aarch64-gnu-debug/Dockerfile#L32 ### Affecting how tests are built From 312d89f69b76091f9bb45503e8e68698a62a9c09 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 7 Sep 2026 13:52:07 +0200 Subject: [PATCH 05/11] further lengthen lines that are to short --- src/doc/rustc-dev-guide/ci/sembr/src/main.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 83094681e9cc4..6d2e78258f85c 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -182,6 +182,15 @@ fn lengthen_lines(content: &str, limit: usize) -> String { new_content[new_n] = format!("{line} {}", next_line.trim_start()); new_content.remove(new_n + 1); skip_next = true; + } else { + const SEP: &str = ", "; + let Some((before_comma, after_comma)) = next_line.split_once(SEP) else { continue }; + if line.len() + before_comma.len() < limit - SEP.len() { + new_content[new_n] = format!("{line} {before_comma}{}", SEP.trim_end()); + new_n += 1; + new_content[new_n] = after_comma.to_owned(); + skip_next = true; + } } } new_content.join("\n") + "\n" @@ -325,7 +334,6 @@ fn should_pass() { } #[test] -#[ignore] fn split_on_comma() { let original = " Because of canonicalization of regions and From 767685d12a1326bf7fa516194af208db08d32b3c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 7 Sep 2026 13:52:25 +0200 Subject: [PATCH 06/11] sembr src/queries/salsa.md --- src/doc/rustc-dev-guide/src/queries/salsa.md | 104 ++++++++++--------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/queries/salsa.md b/src/doc/rustc-dev-guide/src/queries/salsa.md index dc7160edc22cd..5ab289b467ae2 100644 --- a/src/doc/rustc-dev-guide/src/queries/salsa.md +++ b/src/doc/rustc-dev-guide/src/queries/salsa.md @@ -2,10 +2,9 @@ This chapter is based on the explanation given by Niko Matsakis in this [video](https://www.youtube.com/watch?v=_muY4HjSqVw) about -[Salsa](https://github.com/salsa-rs/salsa). To find out more you may -want to watch [Salsa In More -Depth](https://www.youtube.com/watch?v=i_IhACacPRY), also by Niko -Matsakis. +[Salsa](https://github.com/salsa-rs/salsa). +To find out more you may want to watch [Salsa In More +Depth](https://www.youtube.com/watch?v=i_IhACacPRY), also by Niko Matsakis. > As of November 2022, although Salsa is inspired by (among > other things) rustc's query system, it is not used directly in rustc. It @@ -19,9 +18,9 @@ Matsakis. ## What is Salsa? -Salsa is a library for incremental recomputation. This means it allows reusing -computations that were already done in the past to increase the efficiency -of future computations. +Salsa is a library for incremental recomputation. +This means it allows reusing +computations that were already done in the past to increase the efficiency of future computations. The objectives of Salsa are: * Provide that functionality in an automatic way, so reusing old computations @@ -32,8 +31,9 @@ The objectives of Salsa are: Salsa's actual model is much richer, allowing many kinds of inputs and many different outputs. For example, integrating Salsa with an IDE could mean that the inputs could be manifests (`Cargo.toml`, `rust-toolchain.toml`), entire -source files (`foo.rs`), snippets and so on. The outputs of such an integration -could range from a binary executable, to lints, types (for example, if a user +source files (`foo.rs`), snippets and so on. +The outputs of such an integration could range from a binary executable, +to lints, types (for example, if a user selects a certain variable and wishes to see its type), completions, etc. ## How does it work? @@ -45,26 +45,27 @@ Then Salsa has to also identify intermediate, "derived" values, which are something that the library produces, but, for each derived value there's a "pure" function that computes the derived value. -For example, there might be a function `ast(x: Path) -> AST`. The produced -Abstract Syntax Tree (`AST`) isn't a final value, it's an intermediate value +For example, there might be a function `ast(x: Path) -> AST`. +The produced Abstract Syntax Tree (`AST`) isn't a final value, it's an intermediate value that the library would use for the computation. This means that when you try to compute with the library, Salsa is going to compute various derived values, and eventually read the input and produce the result for the asked computation. -In the course of computing, Salsa tracks which inputs were accessed and which -values are derived. This information is used to determine what's going to +In the course of computing, Salsa tracks which inputs were accessed and which values are derived. +This information is used to determine what's going to happen when the inputs change: are the derived values still valid? This doesn't necessarily mean that each computation downstream from the input -is going to be checked, which could be costly. Salsa only needs to check each -downstream computation until it finds one that isn't changed. At that point, it -won't check other derived computations since they wouldn't need to change. +is going to be checked, which could be costly. +Salsa only needs to check each downstream computation until it finds one that isn't changed. +At that point, it won't check other derived computations since they wouldn't need to change. -It's helpful to think about this as a graph with nodes. Each derived value -has a dependency on other values, which could themselves be either base or -derived. Base values don't have a dependency. +It's helpful to think about this as a graph with nodes. +Each derived value has a dependency on other values, which could themselves be either base or +derived. +Base values don't have a dependency. ```ignore I <- A <- C ... @@ -72,49 +73,52 @@ I <- A <- C ... J <- B <--+ ``` -When an input `I` changes, the derived value `A` could change. The derived -value `B`, which does not depend on `I`, `A`, or any value derived from `A` or -`I`, is not subject to change. Therefore, Salsa can reuse the computation done -for `B` in the past, without having to compute it again. - -The computation could also terminate early. Keeping the same graph as before, -say that input `I` has changed in some way (and input `J` hasn't), but when -computing `A` again, it's found that `A` hasn't changed from the previous -computation. This leads to an "early termination", because there's no need to -check if `C` needs to change, since both `C` direct inputs, `A` and `B`, +When an input `I` changes, the derived value `A` could change. +The derived value `B`, which does not depend on `I`, `A`, or any value derived from `A` or +`I`, is not subject to change. + Therefore, Salsa can reuse the computation done for `B` in the past, +without having to compute it again. + +The computation could also terminate early. +Keeping the same graph as before, +say that input `I` has changed in some way (and input `J` hasn't), but when computing `A` again, +it's found that `A` hasn't changed from the previous +computation. +This leads to an "early termination", because there's no need to check if `C` needs to change, +since both `C` direct inputs, `A` and `B`, haven't changed. ## Key Salsa concepts ### Query -A query is some value that Salsa can access in the course of computation. Each -query can have a number of keys (from 0 to many), and all queries have a -result, akin to functions. `0-key` queries are called "input" queries. +A query is some value that Salsa can access in the course of computation. + Each query can have a number of keys (from 0 to many), and all queries have a +result, akin to functions. + `0-key` queries are called "input" queries. ### Database The database is basically the context for the entire computation, it's meant to store Salsa's internal state, all intermediate values for each query, and -anything else that the computation might need. The database must know all the -queries the library is going to do before it can be built, but they don't need +anything else that the computation might need. +The database must know all the queries the library is going to do before it can be built, +but they don't need to be specified in the same place. -After the database is formed, it can be accessed with queries that are very -similar to functions. Since each query's result is stored in the database, when -a query is invoked `N`-times, it will return `N`-**cloned** results, without having -to recompute the query (unless the input has changed in such a way that it -warrants recomputation). +After the database is formed, it can be accessed with queries that are very similar to functions. +Since each query's result is stored in the database, when a query is invoked `N`-times, +it will return `N`-**cloned** results, without having +to recompute the query (unless the input has changed in such a way that it warrants recomputation). For each input query (`0-key`), a "set" method is generated, allowing the user to -change the output of such query, and trigger previous memoized values to be -potentially invalidated. +change the output of such query, and trigger previous memoized values to be potentially invalidated. ### Query Groups A query group is a set of queries which have been defined together as a unit. -The database is formed by combining query groups. Query groups are akin to -"Salsa modules". +The database is formed by combining query groups. +Query groups are akin to "Salsa modules". A set of queries in a query group are just a set of methods in a trait. @@ -146,8 +150,7 @@ pub trait Inputs { ``` To create a **derived** query group, one must specify which other query groups -this one depends on by specifying them as supertraits, as seen in the following -example: +this one depends on by specifying them as supertraits, as seen in the following example: ```rust,ignore /// This query group is going to contain queries that depend on derived values. @@ -162,9 +165,9 @@ pub trait Parser: Inputs { } ``` -When creating a derived query the implementation of said query must be defined -outside the trait. The definition must take a database parameter as an `impl -Trait` (or `dyn Trait`), where trait is the query group that the definition +When creating a derived query the implementation of said query must be defined outside the trait. + The definition must take a database parameter as an `impl Trait` (or `dyn Trait`), +where trait is the query group that the definition belongs to, in addition to the other keys. ```rust,ignore @@ -187,8 +190,9 @@ Eventually, after all the query groups have been defined, the database can be created by declaring a `struct`. To specify which query groups are going to be part of the database an `attribute` -(`#[salsa::database(...)]`) must be added. The argument of said `attribute` is a -list of `identifiers`, specifying the query groups **storages**. +(`#[salsa::database(...)]`) must be added. +The argument of said `attribute` is a list of `identifiers`, +specifying the query groups **storages**. ```rust,ignore ///This attribute specifies which query groups are going to be in the database From 18dbb539e5d2362c2d3631f5034eac9ed2b93414 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 7 Sep 2026 14:27:56 +0200 Subject: [PATCH 07/11] improve queries/salsa.md --- src/doc/rustc-dev-guide/ci/sembr/src/main.rs | 16 +++++++++++- src/doc/rustc-dev-guide/src/queries/salsa.md | 26 ++++++++++---------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 6d2e78258f85c..82c096c4bbd11 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -334,7 +334,21 @@ fn should_pass() { } #[test] -fn split_on_comma() { +#[ignore] +fn split_on_comma_of_current_line() { + let original = " +Each derived value has a dependency on other values, which could themselves be either base or +derived. +"; + let expected = " +Each derived value has a dependency on other values, +which could themselves be either base or derived. +"; + assert_eq!(expected, lengthen_lines(original, 100)) +} + +#[test] +fn split_on_comma_of_next_line() { let original = " Because of canonicalization of regions and inference variables, encountering a cycle doesn't mean that we would get an infinite proof tree. diff --git a/src/doc/rustc-dev-guide/src/queries/salsa.md b/src/doc/rustc-dev-guide/src/queries/salsa.md index 5ab289b467ae2..fcd67de72b206 100644 --- a/src/doc/rustc-dev-guide/src/queries/salsa.md +++ b/src/doc/rustc-dev-guide/src/queries/salsa.md @@ -19,8 +19,8 @@ Depth](https://www.youtube.com/watch?v=i_IhACacPRY), also by Niko Matsakis. ## What is Salsa? Salsa is a library for incremental recomputation. -This means it allows reusing -computations that were already done in the past to increase the efficiency of future computations. +This means it allows reusing computations that were already done in the past +to increase the efficiency of future computations. The objectives of Salsa are: * Provide that functionality in an automatic way, so reusing old computations @@ -30,11 +30,11 @@ The objectives of Salsa are: Salsa's actual model is much richer, allowing many kinds of inputs and many different outputs. For example, integrating Salsa with an IDE could mean that -the inputs could be manifests (`Cargo.toml`, `rust-toolchain.toml`), entire -source files (`foo.rs`), snippets and so on. +the inputs could be manifests (`Cargo.toml`, `rust-toolchain.toml`), +entire source files (`foo.rs`), snippets, and so on. The outputs of such an integration could range from a binary executable, -to lints, types (for example, if a user -selects a certain variable and wishes to see its type), completions, etc. +to lints, types (for example, if a user selects a certain variable and wishes to see its type), +completions, etc. ## How does it work? @@ -46,8 +46,8 @@ something that the library produces, but, for each derived value there's a "pure" function that computes the derived value. For example, there might be a function `ast(x: Path) -> AST`. -The produced Abstract Syntax Tree (`AST`) isn't a final value, it's an intermediate value -that the library would use for the computation. +The produced Abstract Syntax Tree (`AST`) isn't a final value; +it's an intermediate value that the library would use for the computation. This means that when you try to compute with the library, Salsa is going to compute various derived values, and eventually read the input and produce the @@ -76,7 +76,7 @@ J <- B <--+ When an input `I` changes, the derived value `A` could change. The derived value `B`, which does not depend on `I`, `A`, or any value derived from `A` or `I`, is not subject to change. - Therefore, Salsa can reuse the computation done for `B` in the past, +Therefore, Salsa can reuse the computation done for `B` in the past, without having to compute it again. The computation could also terminate early. @@ -93,9 +93,9 @@ haven't changed. ### Query A query is some value that Salsa can access in the course of computation. - Each query can have a number of keys (from 0 to many), and all queries have a +Each query can have a number of keys (from 0 to many), and all queries have a result, akin to functions. - `0-key` queries are called "input" queries. +`0-key` queries are called "input" queries. ### Database @@ -165,8 +165,8 @@ pub trait Parser: Inputs { } ``` -When creating a derived query the implementation of said query must be defined outside the trait. - The definition must take a database parameter as an `impl Trait` (or `dyn Trait`), +When creating a derived query, the implementation of said query must be defined outside the trait. +The definition must take a database parameter as an `impl Trait` (or `dyn Trait`), where trait is the query group that the definition belongs to, in addition to the other keys. From fa088e763df4ad19b120ac320c5f91b1d91eb3db Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 7 Sep 2026 14:33:12 +0200 Subject: [PATCH 08/11] sembr src/queries/query-evaluation-model-in-detail.md --- .../query-evaluation-model-in-detail.md | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md b/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md index c1a4373f7dac6..6461208fe2eaa 100644 --- a/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md +++ b/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md @@ -1,8 +1,8 @@ # The Query Evaluation Model in detail This chapter provides a deeper dive into the abstract model queries are built on. -It does not go into implementation details but tries to explain -the underlying logic. The examples here, therefore, have been stripped down and +It does not go into implementation details but tries to explain the underlying logic. +The examples here, therefore, have been stripped down and simplified and don't directly reflect the compilers internal APIs. ## What is a query? @@ -12,9 +12,9 @@ and queries are the way of asking the compiler questions about it, i.e. we "query" the compiler's "database" for facts. However, there's something special to this compiler database: It starts out empty -and is filled on-demand when queries are executed. Consequently, a query must -know how to compute its result if the database does not contain it yet. For -doing so, it can access other queries and certain input values that the database +and is filled on-demand when queries are executed. +Consequently, a query must know how to compute its result if the database does not contain it yet. +For doing so, it can access other queries and certain input values that the database is pre-filled with on creation. A query thus consists of the following things: @@ -26,14 +26,12 @@ A query thus consists of the following things: computed if it isn't already present in the database. As an example, the name of the `type_of` query is `type_of`, its query key is a -`DefId` identifying the item we want to know the type of, the result type is -`Ty<'tcx>`, and the provider is a function that, given the query key and access -to the rest of the database, can compute the type of the item identified by the -key. +`DefId` identifying the item we want to know the type of, the result type is `Ty<'tcx>`, +and the provider is a function that, given the query key and access +to the rest of the database, can compute the type of the item identified by the key. -So in some sense a query is just a function that maps the query key to the -corresponding result. However, we have to apply some restrictions in order for -this to be sound: +So in some sense a query is just a function that maps the query key to the corresponding result. +However, we have to apply some restrictions in order for this to be sound: - The key and result must be immutable values. - The provider function must be a pure function in the sense that for the same @@ -41,9 +39,11 @@ this to be sound: - The only parameters a provider function takes are the key and a reference to the "query context" (which provides access to the rest of the "database"). -The database is built up lazily by invoking queries. The query providers will -invoke other queries, for which the result is either already cached or computed -by calling another query provider. These query provider invocations +The database is built up lazily by invoking queries. +The query providers will invoke other queries, +for which the result is either already cached or computed +by calling another query provider. +These query provider invocations conceptually form a directed acyclic graph (DAG) at the leaves of which are input values that are already known when the query context is created. @@ -56,12 +56,12 @@ will cache the result in an internal table and, when the query is invoked with the same query key again, will return the result from the cache instead of running the provider again. -This caching is crucial for making the query engine efficient. Without -memoization the system would still be sound (that is, it would yield the same +This caching is crucial for making the query engine efficient. +Without memoization the system would still be sound (that is, it would yield the same results) but the same computations would be done over and over again. -Memoization is one of the main reasons why query providers have to be pure -functions. If calling a provider function could yield different results for +Memoization is one of the main reasons why query providers have to be pure functions. +If calling a provider function could yield different results for each invocation (because it accesses some global mutable state) then we could not memoize the result. @@ -69,8 +69,9 @@ not memoize the result. ## Input data -When the query context is created, it is still empty: No queries have been -executed, no results are cached. But the context already provides access to +When the query context is created, it is still empty: No queries have been executed, +no results are cached. +But the context already provides access to "input" data, i.e. pieces of immutable data that were computed before the context was created and that queries can access to do their computations. @@ -85,18 +86,19 @@ result from (remember, query providers only have access to other queries and the context but not any other outside state or information). For a query provider, input data and results of other queries look exactly the -same: It just tells the context "give me the value of X". Because input data -is immutable, the provider can rely on it being the same across +same: It just tells the context "give me the value of X". +Because input data is immutable, the provider can rely on it being the same across different query invocations, just as is the case for query results. ## An example execution trace of some queries -How does this DAG of query invocations come into existence? At some point -the compiler driver will create the, as yet empty, query context. It will then, -from outside of the query system, invoke the queries it needs to perform its -task. This looks something like the following: +How does this DAG of query invocations come into existence? +At some point the compiler driver will create the, as yet empty, query context. +It will then, +from outside of the query system, invoke the queries it needs to perform its task. +This looks something like the following: ```rust,ignore fn compile_crate() { @@ -124,9 +126,8 @@ fn type_check_crate_provider(tcx, _key: ()) { ``` We see that the `type_check_crate` query accesses input data -(`tcx.hir_map.list_of_items()`) and invokes other queries -(`type_check_item`). The `type_check_item` -invocations will themselves access input data and/or invoke other queries, +(`tcx.hir_map.list_of_items()`) and invokes other queries (`type_check_item`). +The `type_check_item` invocations will themselves access input data and/or invoke other queries, so that in the end the DAG of query invocations will be built up backwards from the node that was initially executed: @@ -146,8 +147,8 @@ from the node that was initially executed: ``` We also see that often a query result can be read from the cache: -`type_of(bar)` was computed for `type_check_item(foo)` so when -`type_check_item(bar)` needs it, it is already in the cache. +`type_of(bar)` was computed for `type_check_item(foo)` so when `type_check_item(bar)` needs it, +it is already in the cache. Query results stay cached in the query context as long as the context lives. So if the compiler driver invoked another query later on, the above graph @@ -157,8 +158,8 @@ would still exist and already executed queries would not have to be re-done. ## Cycles -Earlier we stated that query invocations form a DAG. However, it would be easy -to form a cyclic graph by, for example, having a query provider like the +Earlier we stated that query invocations form a DAG. +However, it would be easy to form a cyclic graph by, for example, having a query provider like the following: ```rust,ignore @@ -169,24 +170,24 @@ fn cyclic_query_provider(tcx, key) -> u32 { ``` Since query providers are regular functions, this would behave much as expected: -Evaluation would get stuck in an infinite recursion. A query like this would not -be very useful either. However, sometimes certain kinds of invalid user input -can result in queries being called in a cyclic way. The query engine includes -a check for cyclic invocations of queries with the same input arguments. +Evaluation would get stuck in an infinite recursion. +A query like this would not be very useful either. +However, sometimes certain kinds of invalid user input +can result in queries being called in a cyclic way. +The query engine includes a check for cyclic invocations of queries with the same input arguments. And, because cycles are an irrecoverable error, will abort execution with a "cycle error" message that tries to be human readable. At some point the compiler had a notion of "cycle recovery", that is, one could -"try" to execute a query and if it ended up causing a cycle, proceed in some -other fashion. However, this was later removed because it is not entirely -clear what the theoretical consequences of this are, especially regarding -incremental compilation. +"try" to execute a query and if it ended up causing a cycle, proceed in some other fashion. +However, this was later removed because it is not entirely +clear what the theoretical consequences of this are, especially regarding incremental compilation. ## "Steal" Queries -Some queries have their result wrapped in a `Steal` struct. These queries -behave exactly the same as regular with one exception: Their result is expected +Some queries have their result wrapped in a `Steal` struct. +These queries behave exactly the same as regular with one exception: Their result is expected to be "stolen" out of the cache at some point, meaning some other part of the program is taking ownership of it and the result cannot be accessed anymore. @@ -194,18 +195,18 @@ This stealing mechanism exists purely as a performance optimization because some result values are too costly to clone (e.g. the MIR of a function). It seems like result stealing would violate the condition that query results must be immutable (after all we are moving the result value out of the cache) but it is -OK as long as the mutation is not observable. This is achieved by two things: +OK as long as the mutation is not observable. +This is achieved by two things: - Before a result is stolen, we make sure to eagerly run all queries that - might ever need to read that result. This has to be done manually by calling - those queries. + might ever need to read that result. + This has to be done manually by calling those queries. - Whenever a query tries to access a stolen result, we make an ICE (Internal Compiler Error) so that such a condition cannot go unnoticed. This is not an ideal setup because of the manual intervention needed, so it -should be used sparingly and only when it is well known which queries might -access a given result. In practice, however, stealing has not turned out to be -much of a maintenance burden. +should be used sparingly and only when it is well known which queries might access a given result. +In practice, however, stealing has not turned out to be much of a maintenance burden. To summarize: "Steal queries" break some of the rules in a controlled way. There are checks in place that make sure that nothing can go silently wrong. From f4fa95009724af4ea4b9ef8dec59c4ec9508e384 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 7 Sep 2026 14:46:26 +0200 Subject: [PATCH 09/11] improve queries/query-evaluation-model-in-detail.md --- .../queries/query-evaluation-model-in-detail.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md b/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md index 6461208fe2eaa..443bcea4ee323 100644 --- a/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md +++ b/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md @@ -2,8 +2,8 @@ This chapter provides a deeper dive into the abstract model queries are built on. It does not go into implementation details but tries to explain the underlying logic. -The examples here, therefore, have been stripped down and -simplified and don't directly reflect the compilers internal APIs. +The examples here, therefore, have been stripped down and simplified, +and don't directly reflect compiler internal APIs. ## What is a query? @@ -57,7 +57,7 @@ the same query key again, will return the result from the cache instead of running the provider again. This caching is crucial for making the query engine efficient. -Without memoization the system would still be sound (that is, it would yield the same +Without memoization, the system would still be sound (that is, it would yield the same results) but the same computations would be done over and over again. Memoization is one of the main reasons why query providers have to be pure functions. @@ -71,8 +71,8 @@ not memoize the result. When the query context is created, it is still empty: No queries have been executed, no results are cached. -But the context already provides access to -"input" data, i.e. pieces of immutable data that were computed before the +But the context already provides access to "input" data, +i.e. pieces of immutable data that were computed before the context was created and that queries can access to do their computations. As of January 2021, this input data consists mainly of @@ -175,11 +175,12 @@ A query like this would not be very useful either. However, sometimes certain kinds of invalid user input can result in queries being called in a cyclic way. The query engine includes a check for cyclic invocations of queries with the same input arguments. -And, because cycles are an irrecoverable error, will abort execution with a +And, because cycles are an irrecoverable error, will abort execution with a "cycle error" message that tries to be human readable. -At some point the compiler had a notion of "cycle recovery", that is, one could -"try" to execute a query and if it ended up causing a cycle, proceed in some other fashion. +At some point the compiler had a notion of "cycle recovery". +That is, one could "try" to execute a query, +and if it ended up causing a cycle, proceed in some other fashion. However, this was later removed because it is not entirely clear what the theoretical consequences of this are, especially regarding incremental compilation. From 1ec54a77b65eebc6bf5ab12feea2ecc5fb013719 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 7 Sep 2026 14:47:14 +0200 Subject: [PATCH 10/11] sembr src/queries/incremental-compilation.md --- .../src/queries/incremental-compilation.md | 124 +++++++++--------- 1 file changed, 59 insertions(+), 65 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md b/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md index 731ff3287d9fe..ebddbe9454635 100644 --- a/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md +++ b/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md @@ -1,21 +1,20 @@ # Incremental compilation The incremental compilation scheme is, in essence, a surprisingly -simple extension to the overall query system. We'll start by describing +simple extension to the overall query system. +We'll start by describing a slightly simplified variant of the real thing – the "basic algorithm" – and then describe some possible improvements. ## The basic algorithm -The basic algorithm is -called the **red-green** algorithm[^salsa]. The high-level idea is -that, after each run of the compiler, we will save the results of all +The basic algorithm is called the **red-green** algorithm[^salsa]. +The high-level idea is that, after each run of the compiler, we will save the results of all the queries that we do, as well as the **query DAG**. The -**query DAG** is a [DAG] that indexes which queries executed which -other queries. So, for example, there would be an [edge] from a query Q1 +**query DAG** is a [DAG] that indexes which queries executed which other queries. +So, for example, there would be an [edge] from a query Q1 to another query Q2 if computing Q1 required computing Q2 (note that -because queries cannot depend on themselves, this results in a DAG and -not a general graph). +because queries cannot depend on themselves, this results in a DAG and not a general graph). [DAG]: https://en.wikipedia.org/wiki/Directed_acyclic_graph @@ -34,8 +33,8 @@ not a general graph). > but also for a specific instance of that query with given arguments. On the next run of the compiler, then, we can sometimes reuse these -query results to avoid re-executing a query. We do this by assigning -every query a **color**: +query results to avoid re-executing a query. +We do this by assigning every query a **color**: - If a query is colored **red**, that means that its result during this compilation has **changed** from the previous compilation. @@ -48,62 +47,57 @@ There are two key insights here: query Q **must** result in the same value as last time and hence need not be re-executed (or else the compiler is not deterministic). - Second, even if some inputs to a query changes, it may be that it - **still** produces the same result as the previous compilation. In - particular, the query may only use part of its input. + **still** produces the same result as the previous compilation. + In particular, the query may only use part of its input. - Therefore, after executing a query, we always check whether it - produced the same result as the previous time. **If it did,** we - can still mark the query as green, and hence avoid re-executing + produced the same result as the previous time. + **If it did,** we can still mark the query as green, and hence avoid re-executing dependent queries. ### The try-mark-green algorithm -At the core of incremental compilation is an algorithm called -"try-mark-green". It has the job of determining the color of a given -query Q (which must not have yet been executed). In cases where Q has -red inputs, determining Q's color may involve re-executing Q so that +At the core of incremental compilation is an algorithm called "try-mark-green". +It has the job of determining the color of a given query Q (which must not have yet been executed). +In cases where Q has red inputs, determining Q's color may involve re-executing Q so that we can compare its output, but if all of Q's inputs are green, then we -can conclude that Q must be green without re-executing it or inspecting -its value at all. In the compiler, this allows us to avoid -deserializing the result from disk when we don't need it, and in fact -enables us to sometimes skip *serializing* the result as well -(see the refinements section below). +can conclude that Q must be green without re-executing it or inspecting its value at all. +In the compiler, this allows us to avoid deserializing the result from disk when we don't need it, +and in fact +enables us to sometimes skip *serializing* the result as well (see the refinements section below). Try-mark-green works as follows: - First check if the query Q was executed during the previous compilation. - - If not, we can just re-execute the query as normal, and assign it the - color of red. + - If not, we can just re-execute the query as normal, and assign it the color of red. - If yes, then load the 'dependent queries' of Q. -- If there is a saved result, then we load the `reads(Q)` vector from the - query DAG. The "reads" is the set of queries that Q executed during - its execution. - - For each query R in `reads(Q)`, we recursively demand the color - of R using try-mark-green. +- If there is a saved result, then we load the `reads(Q)` vector from the query DAG. + The "reads" is the set of queries that Q executed during its execution. + - For each query R in `reads(Q)`, we recursively demand the color of R using try-mark-green. - Note: it is important that we visit each node in `reads(Q)` in same order - as they occurred in the original compilation. See [the section on the - query DAG below](#dag). - - If **any** of the nodes in `reads(Q)` wind up colored **red**, then Q is - dirty. + as they occurred in the original compilation. + See [the section on the query DAG below](#dag). + - If **any** of the nodes in `reads(Q)` wind up colored **red**, then Q is dirty. - We re-execute Q and compare the hash of its result to the hash of the result from the previous compilation. - If the hash has not changed, we can mark Q as **green** and return. - - Otherwise, **all** of the nodes in `reads(Q)` must be **green**. In that - case, we can color Q as **green** and return. + - Otherwise, **all** of the nodes in `reads(Q)` must be **green**. In that case, +we can color Q as **green** and return. ### The query DAG -The query DAG code is stored in -[`compiler/rustc_middle/src/dep_graph`][dep_graph]. Construction of the DAG is done -by instrumenting the query execution. +The query DAG code is stored in [`compiler/rustc_middle/src/dep_graph`][dep_graph]. +Construction of the DAG is done by instrumenting the query execution. -One key point is that the query DAG also tracks ordering; that is, for -each query Q, we not only track the queries that Q reads, we track the -**order** in which they were read. This allows try-mark-green to walk -those queries back in the same order. This is important because once a -subquery comes back as red, we can no longer be sure that Q will continue -along the same path as before. That is, imagine a query like this: +One key point is that the query DAG also tracks ordering; that is, for each query Q, +we not only track the queries that Q reads, we track the +**order** in which they were read. + This allows try-mark-green to walk those queries back in the same order. +This is important because once a subquery comes back as red, +we can no longer be sure that Q will continue +along the same path as before. +That is, imagine a query like this: ```rust,ignore fn main_query(tcx) { @@ -115,15 +109,15 @@ fn main_query(tcx) { } ``` -Now imagine that in the first compilation, `main_query` starts by -executing `subquery1`, and this returns true. In that case, the next -query `main_query` executes will be `subquery2`, and `subquery3` will +Now imagine that in the first compilation, `main_query` starts by executing `subquery1`, +and this returns true. +In that case, the next query `main_query` executes will be `subquery2`, and `subquery3` will not be executed at all. But now imagine that in the **next** compilation, the input has -changed such that `subquery1` returns **false**. In this case, `subquery2` -would never execute. If try-mark-green were to visit `reads(main_query)` out -of order, however, it might visit `subquery2` before `subquery1`, and hence +changed such that `subquery1` returns **false**. In this case, `subquery2` would never execute. +If try-mark-green were to visit `reads(main_query)` out of order, +however, it might visit `subquery2` before `subquery1`, and hence execute it. This can lead to ICEs and other problems in the compiler. @@ -132,28 +126,28 @@ This can lead to ICEs and other problems in the compiler. ## Improvements to the basic algorithm In the description of the basic algorithm, we said that at the end of -compilation we would save the results of all the queries that were -performed. In practice, this can be quite wasteful – many of those -results are very cheap to recompute, and serializing and deserializing -them is not a particular win. In practice, what we would do is to save -**the hashes** of all the subqueries that we performed. Then, in select cases, +compilation we would save the results of all the queries that were performed. + In practice, this can be quite wasteful – many of those results are very cheap to recompute, +and serializing and deserializing +them is not a particular win. +In practice, what we would do is to save **the hashes** of all the subqueries that we performed. +Then, in select cases, we **also** save the results. -This is why the incremental algorithm separates computing the -**color** of a node, which often does not require its value, from -computing the **result** of a node. Computing the result is done via a simple -algorithm like so: +This is why the incremental algorithm separates computing the **color** of a node, +which often does not require its value, from +computing the **result** of a node. +Computing the result is done via a simple algorithm like so: -- Check if a saved result for Q is available. If so, compute the color of Q. +- Check if a saved result for Q is available. + If so, compute the color of Q. If Q is green, deserialize and return the saved result. - Otherwise, execute Q. - - We can then compare the hash of the result and color Q as green if - it did not change. + - We can then compare the hash of the result and color Q as green if it did not change. ## Resources The initial design document can be found [here][initial-design], which expands -on the memoization details, provides more high-level overview and motivation -for this system. +on the memoization details, provides more high-level overview and motivation for this system. # Footnotes From 202362f973e56dfec7f83750904ef1372fc96d8d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 7 Sep 2026 15:19:53 +0200 Subject: [PATCH 11/11] improve queries/incremental-compilation.md --- src/doc/rustc-dev-guide/ci/sembr/src/main.rs | 8 +++++ .../src/queries/incremental-compilation.md | 34 +++++++++---------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 82c096c4bbd11..92143aaa1fb87 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -359,3 +359,11 @@ encountering a cycle doesn't mean that we would get an infinite proof tree. "; assert_eq!(expected, lengthen_lines(original, 100)) } + +#[test] +#[ignore] +fn should_split() { + let original = "the queries that we do, as well as the **query DAG**. The"; + let expected = "the queries that we do, as well as the **query DAG**.\nThe\n"; + assert_eq!(expected, comply(original)); +} diff --git a/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md b/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md index ebddbe9454635..079a2b2e00a7f 100644 --- a/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md +++ b/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md @@ -19,17 +19,17 @@ because queries cannot depend on themselves, this results in a DAG and not a gen [DAG]: https://en.wikipedia.org/wiki/Directed_acyclic_graph > **NOTE**: You might think of a query as simply the definition of a query. -> A thing that you can invoke, a bit like a function, +> A thing that you can invoke, a bit like a function, > and which either returns a cached result or actually executes the code. -> +> > If that's the way you think about queries, -> it's good to know that in the following text, queries will be said to have colours. -> Keep in mind though, that here the word query also refers to a certain invocation of -> the query for a certain input. As you will read later, queries are fingerprinted based -> on their arguments. The result of a query might change when we give it one argument +> it's good to know that in the following text, queries will be said to have colours. +> Keep in mind though, that here the word query also refers to a certain invocation of +> the query for a certain input. As you will read later, queries are fingerprinted based +> on their arguments. The result of a query might change when we give it one argument > and be coloured red, while it stays the same for another argument and is thus green. -> -> In short, the word query is here not just used to mean the definition of a query, +> +> In short, the word query is here not just used to mean the definition of a query, > but also for a specific instance of that query with given arguments. On the next run of the compiler, then, we can sometimes reuse these @@ -80,8 +80,8 @@ Try-mark-green works as follows: - We re-execute Q and compare the hash of its result to the hash of the result from the previous compilation. - If the hash has not changed, we can mark Q as **green** and return. - - Otherwise, **all** of the nodes in `reads(Q)` must be **green**. In that case, -we can color Q as **green** and return. + - Otherwise, **all** of the nodes in `reads(Q)` must be **green**. + In that case, we can color Q as **green** and return. @@ -91,9 +91,9 @@ The query DAG code is stored in [`compiler/rustc_middle/src/dep_graph`][dep_grap Construction of the DAG is done by instrumenting the query execution. One key point is that the query DAG also tracks ordering; that is, for each query Q, -we not only track the queries that Q reads, we track the -**order** in which they were read. - This allows try-mark-green to walk those queries back in the same order. +we not only track the queries that Q reads; +we also track the **order** in which they were read. +This allows try-mark-green to walk those queries back in the same order. This is important because once a subquery comes back as red, we can no longer be sure that Q will continue along the same path as before. @@ -127,12 +127,10 @@ This can lead to ICEs and other problems in the compiler. In the description of the basic algorithm, we said that at the end of compilation we would save the results of all the queries that were performed. - In practice, this can be quite wasteful – many of those results are very cheap to recompute, -and serializing and deserializing -them is not a particular win. +In practice, this can be quite wasteful – many of those results are very cheap to recompute, +and serializing and deserializing them is not a particular win. In practice, what we would do is to save **the hashes** of all the subqueries that we performed. -Then, in select cases, -we **also** save the results. +Then, in select cases, we **also** save the results. This is why the incremental algorithm separates computing the **color** of a node, which often does not require its value, from