Rollup merge of #139283 - BoxyUwU:rdg-push, r=jieyouxu

Rustc dev guide subtree update

r? ``@jieyouxu`` ``@Kobzol``
This commit is contained in:
Matthias Krüger 2025-04-03 07:39:08 +02:00 committed by GitHub
commit 85c557e76e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 543 additions and 410 deletions

View File

@ -18,6 +18,7 @@ jobs:
MDBOOK_LINKCHECK2_VERSION: 0.9.1
MDBOOK_MERMAID_VERSION: 0.12.6
MDBOOK_TOC_VERSION: 0.11.2
MDBOOK_OUTPUT__LINKCHECK__FOLLOW_WEB_LINKS: ${{ github.event_name != 'pull_request' }}
DEPLOY_DIR: book/html
BASE_SHA: ${{ github.event.pull_request.base.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -62,5 +62,7 @@ warning-policy = "error"
"/diagnostics/sessiondiagnostic.html" = "diagnostic-structs.html"
"/diagnostics/diagnostic-codes.html" = "error-codes.html"
"/miri.html" = "const-eval/interpret.html"
"/tests/integration.html" = "ecosystem.html"
"/tests/fuchsia.html" = "ecosystem-test-jobs/fuchsia.html"
"/tests/headers.html" = "directives.html"
"/tests/integration.html" = "ecosystem.html"
"/tests/rust-for-linux.html" = "ecosystem-test-jobs/rust-for-linux.html"

View File

@ -1,11 +1,8 @@
use std::{
collections::BTreeMap,
convert::TryInto as _,
env, fmt, fs,
path::{Path, PathBuf},
process,
str::FromStr,
};
use std::collections::BTreeMap;
use std::convert::TryInto as _;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{env, fmt, fs, process};
use chrono::{Datelike as _, Month, TimeZone as _, Utc};
use glob::glob;
@ -19,19 +16,13 @@ struct Date {
impl Date {
fn months_since(self, other: Date) -> Option<u32> {
let self_chrono = Utc
.with_ymd_and_hms(self.year.try_into().unwrap(), self.month, 1, 0, 0, 0)
.unwrap();
let other_chrono = Utc
.with_ymd_and_hms(other.year.try_into().unwrap(), other.month, 1, 0, 0, 0)
.unwrap();
let self_chrono =
Utc.with_ymd_and_hms(self.year.try_into().unwrap(), self.month, 1, 0, 0, 0).unwrap();
let other_chrono =
Utc.with_ymd_and_hms(other.year.try_into().unwrap(), other.month, 1, 0, 0, 0).unwrap();
let duration_since = self_chrono.signed_duration_since(other_chrono);
let months_since = duration_since.num_days() / 30;
if months_since < 0 {
None
} else {
Some(months_since.try_into().unwrap())
}
if months_since < 0 { None } else { Some(months_since.try_into().unwrap()) }
}
}
@ -66,26 +57,18 @@ fn collect_dates_from_file(date_regex: &Regex, text: &str) -> Vec<(usize, Date)>
date_regex
.captures_iter(text)
.filter_map(|cap| {
if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = (
cap.name("m1"),
cap.name("y1"),
cap.name("m2"),
cap.name("y2"),
) {
if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) =
(cap.name("m1"), cap.name("y1"), cap.name("m2"), cap.name("y2"))
{
let year = year.as_str().parse().expect("year");
let month = Month::from_str(month.as_str())
.expect("month")
.number_from_month();
let month = Month::from_str(month.as_str()).expect("month").number_from_month();
Some((cap.get(0).expect("all").range(), Date { year, month }))
} else {
None
}
})
.map(|(byte_range, date)| {
line += text[end_of_last_cap..byte_range.end]
.chars()
.filter(|c| *c == '\n')
.count();
line += text[end_of_last_cap..byte_range.end].chars().filter(|c| *c == '\n').count();
end_of_last_cap = byte_range.end;
(line, date)
})
@ -138,10 +121,7 @@ fn main() {
let root_dir_path = Path::new(&root_dir);
let glob_pat = format!("{}/**/*.md", root_dir);
let today_chrono = Utc::now().date_naive();
let current_month = Date {
year: today_chrono.year_ce().1,
month: today_chrono.month(),
};
let current_month = Date { year: today_chrono.year_ce().1, month: today_chrono.month() };
let dates_by_file = collect_dates(glob(&glob_pat).unwrap().map(Result::unwrap));
let dates_by_file: BTreeMap<_, _> =
@ -173,10 +153,7 @@ fn main() {
println!();
for (path, dates) in dates_by_file {
println!(
"- {}",
path.strip_prefix(&root_dir_path).unwrap_or(&path).display(),
);
println!("- {}", path.strip_prefix(&root_dir_path).unwrap_or(&path).display(),);
for (line, date) in dates {
println!(" - [ ] line {}: {}", line, date);
}
@ -191,14 +168,8 @@ mod tests {
#[test]
fn test_months_since() {
let date1 = Date {
year: 2020,
month: 3,
};
let date2 = Date {
year: 2021,
month: 1,
};
let date1 = Date { year: 2020, month: 3 };
let date2 = Date { year: 2021, month: 1 };
assert_eq!(date2.months_since(date1), Some(10));
}
@ -273,83 +244,17 @@ Test8
assert_eq!(
collect_dates_from_file(&make_date_regex(), text),
vec![
(
3,
Date {
year: 2021,
month: 1,
}
),
(
6,
Date {
year: 2021,
month: 2,
}
),
(
9,
Date {
year: 2021,
month: 3,
}
),
(
11,
Date {
year: 2021,
month: 4,
}
),
(
17,
Date {
year: 2021,
month: 5,
}
),
(
20,
Date {
year: 2021,
month: 1,
}
),
(
23,
Date {
year: 2021,
month: 2,
}
),
(
26,
Date {
year: 2021,
month: 3,
}
),
(
28,
Date {
year: 2021,
month: 4,
}
),
(
34,
Date {
year: 2021,
month: 5,
}
),
(
38,
Date {
year: 2021,
month: 6,
}
),
(3, Date { year: 2021, month: 1 }),
(6, Date { year: 2021, month: 2 }),
(9, Date { year: 2021, month: 3 }),
(11, Date { year: 2021, month: 4 }),
(17, Date { year: 2021, month: 5 }),
(20, Date { year: 2021, month: 1 }),
(23, Date { year: 2021, month: 2 }),
(26, Date { year: 2021, month: 3 }),
(28, Date { year: 2021, month: 4 }),
(34, Date { year: 2021, month: 5 }),
(38, Date { year: 2021, month: 6 }),
],
);
}

View File

@ -1,4 +1,4 @@
// Tested with nightly-2025-02-13
// Tested with nightly-2025-03-28
#![feature(rustc_private)]
@ -34,9 +34,9 @@ impl rustc_span::source_map::FileLoader for MyFileLoader {
fn read_file(&self, path: &Path) -> io::Result<String> {
if path == Path::new("main.rs") {
Ok(r#"
static MESSAGE: &str = "Hello, World!";
fn main() {
let message = "Hello, World!";
println!("{message}");
println!("{MESSAGE}");
}
"#
.to_string())
@ -71,14 +71,12 @@ impl rustc_driver::Callbacks for MyCallbacks {
fn after_analysis(&mut self, _compiler: &Compiler, tcx: TyCtxt<'_>) -> Compilation {
// Analyze the program and inspect the types of definitions.
for id in tcx.hir().items() {
let hir = tcx.hir();
let item = hir.item(id);
for id in tcx.hir_free_items() {
let item = &tcx.hir_item(id);
match item.kind {
rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn { .. } => {
let name = item.ident;
rustc_hir::ItemKind::Static(ident, ..) | rustc_hir::ItemKind::Fn { ident, .. } => {
let ty = tcx.type_of(item.hir_id().owner.def_id);
println!("{name:?}:\t{ty:?}")
println!("{ident:?}:\t{ty:?}")
}
_ => (),
}

View File

@ -1,4 +1,4 @@
// Tested with nightly-2025-02-13
// Tested with nightly-2025-03-28
#![feature(rustc_private)]
@ -20,7 +20,7 @@ use std::path::Path;
use std::sync::Arc;
use rustc_ast_pretty::pprust::item_to_string;
use rustc_driver::{run_compiler, Compilation};
use rustc_driver::{Compilation, run_compiler};
use rustc_interface::interface::{Compiler, Config};
use rustc_middle::ty::TyCtxt;
@ -70,11 +70,9 @@ impl rustc_driver::Callbacks for MyCallbacks {
}
fn after_analysis(&mut self, _compiler: &Compiler, tcx: TyCtxt<'_>) -> Compilation {
// Every compilation contains a single crate.
let hir_krate = tcx.hir();
// Iterate over the top-level items in the crate, looking for the main function.
for id in hir_krate.items() {
let item = hir_krate.item(id);
for id in tcx.hir_free_items() {
let item = &tcx.hir_item(id);
// Use pattern-matching to find a specific node inside the main function.
if let rustc_hir::ItemKind::Fn { body, .. } = item.kind {
let expr = &tcx.hir_body(body).value;

View File

@ -1,4 +1,4 @@
// Tested with nightly-2025-02-13
// Tested with nightly-2025-03-28
#![feature(rustc_private)]
@ -64,14 +64,13 @@ fn main() {
println!("{krate:?}");
// Analyze the program and inspect the types of definitions.
rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| {
for id in tcx.hir().items() {
let hir = tcx.hir();
let item = hir.item(id);
for id in tcx.hir_free_items() {
let item = tcx.hir_item(id);
match item.kind {
rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn { .. } => {
let name = item.ident;
rustc_hir::ItemKind::Static(ident, ..)
| rustc_hir::ItemKind::Fn { ident, .. } => {
let ty = tcx.type_of(item.hir_id().owner.def_id);
println!("{name:?}:\t{ty:?}")
println!("{ident:?}:\t{ty:?}")
}
_ => (),
}

View File

@ -1,4 +1,4 @@
// Tested with nightly-2025-02-13
// Tested with nightly-2025-03-28
#![feature(rustc_private)]
@ -86,8 +86,10 @@ fn main() {
rustc_interface::run_compiler(config, |compiler| {
let krate = rustc_interface::passes::parse(&compiler.sess);
rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| {
// Run the analysis phase on the local crate to trigger the type error.
let _ = tcx.analysis(());
// Iterate all the items defined and perform type checking.
tcx.par_hir_body_owners(|item_def_id| {
tcx.ensure_ok().typeck(item_def_id);
});
});
// If the compiler has encountered errors when this closure returns, it will abort (!) the program.
// We avoid this by resetting the error count before returning

View File

@ -1 +1 @@
493c38ba371929579fe136df26eccd9516347c7a
ae9173d7dd4a31806c950c90dcc331f1508b4d17

View File

@ -0,0 +1,7 @@
# matches that of rust-lang/rust
style_edition = "2024"
use_small_heuristics = "Max"
merge_derives = false
group_imports = "StdExternalCrate"
imports_granularity = "Module"
use_field_init_shorthand = true

View File

@ -28,8 +28,11 @@
- [Minicore](./tests/minicore.md)
- [Ecosystem testing](./tests/ecosystem.md)
- [Crater](./tests/crater.md)
- [Fuchsia](./tests/fuchsia.md)
- [Rust for Linux](./tests/rust-for-linux.md)
- [Fuchsia](./tests/ecosystem-test-jobs/fuchsia.md)
- [Rust for Linux](./tests/ecosystem-test-jobs/rust-for-linux.md)
- [Codegen backend testing](./tests/codegen-backend-tests/intro.md)
- [Cranelift codegen backend](./tests/codegen-backend-tests/cg_clif.md)
- [GCC codegen backend](./tests/codegen-backend-tests/cg_gcc.md)
- [Performance testing](./tests/perf.md)
- [Suggest tests tool](./tests/suggest-tests.md)
- [Misc info](./tests/misc.md)
@ -61,12 +64,13 @@
- [ARM](notification-groups/arm.md)
- [Cleanup Crew](notification-groups/cleanup-crew.md)
- [Emscripten](notification-groups/emscripten.md)
- [Fuchsia](notification-groups/fuchsia.md)
- [LLVM](notification-groups/llvm.md)
- [RISC-V](notification-groups/risc-v.md)
- [Rust for Linux](notification-groups/rust-for-linux.md)
- [WASI](notification-groups/wasi.md)
- [WebAssembly](notification-groups/wasm.md)
- [Windows](notification-groups/windows.md)
- [Rust for Linux](notification-groups/rust-for-linux.md)
- [Licenses](./licenses.md)
- [Editions](guides/editions.md)
@ -94,7 +98,7 @@
- [Parallel Compilation](./parallel-rustc.md)
- [Rustdoc internals](./rustdoc-internals.md)
- [Search](./rustdoc-internals/search.md)
- [The `rustdoc` test suite](./rustdoc-internals/rustdoc-test-suite.md)
# Source Code Representation
- [Prologue](./part-3-intro.md)
@ -124,6 +128,7 @@
- [rustc_driver and rustc_interface](./rustc-driver/intro.md)
- [Example: Type checking](./rustc-driver/interacting-with-the-ast.md)
- [Example: Getting diagnostics](./rustc-driver/getting-diagnostics.md)
- [Remarks on perma-unstable features](./rustc-driver/remarks-on-perma-unstable-features.md)
- [Errors and Lints](diagnostics.md)
- [Diagnostic and subdiagnostic structs](./diagnostics/diagnostic-structs.md)
- [Translation](./diagnostics/translation.md)
@ -144,10 +149,7 @@
- [ADTs and Generic Arguments](./ty_module/generic_arguments.md)
- [Parameter types/consts/regions](./ty_module/param_ty_const_regions.md)
- [`TypeFolder` and `TypeFoldable`](./ty-fold.md)
- [Parameter Environments](./param_env/param_env_summary.md)
- [What is it?](./param_env/param_env_what_is_it.md)
- [How are `ParamEnv`'s constructed internally](./param_env/param_env_construction_internals.md)
- [Which `ParamEnv` do I use?](./param_env/param_env_acquisition.md)
- [Typing/Param Envs](./typing_parameter_envs.md)
- [Type inference](./type-inference.md)
- [Trait solving](./traits/resolution.md)
- [Higher-ranked trait bounds](./traits/hrtb.md)

View File

@ -40,5 +40,5 @@ Item | Kind | Short description | Chapter |
[Emitting Diagnostics]: ../diagnostics.html
[Macro expansion]: ../macro-expansion.html
[Name resolution]: ../name-resolution.html
[Parameter Environment]: ../param_env/param_env_summary.html
[Parameter Environment]: ../typing_parameter_envs.html
[Trait Solving: Goals and Clauses]: ../traits/goals-and-clauses.html#domain-goals

View File

@ -63,7 +63,7 @@ cd rust
> **NOTE**: A shallow clone limits which `git` commands can be run.
> If you intend to work on and contribute to the compiler, it is
> generally recommended to fully clone the repository [as shown above](#get-the-source-code),
> or to perform a [partial clone](#shallow-clone-the-repository) instead.
> or to perform a [partial clone](#partial-clone-the-repository) instead.
>
> For example, `git bisect` and `git blame` require access to the commit history,
> so they don't work if the repository was cloned with `--depth 1`.

View File

@ -38,4 +38,4 @@ incremental compilation ([see here][config]). This will make compilation take
longer (especially after a rebase), but will save a ton of space from the
incremental caches.
[config]: ./how-to-build-and-run.md#create-a-configtoml
[config]: ./how-to-build-and-run.md#create-a-bootstraptoml

View File

@ -35,7 +35,7 @@ They're the wrappers of the `const_eval` query.
Statics are special; all other functions do not represent statics correctly
and have thus assertions preventing their use on statics.
The `const_eval_*` functions use a [`ParamEnv`](./param_env/param_env_summary.html) of environment
The `const_eval_*` functions use a [`ParamEnv`](./typing_parameter_envs.html) of environment
in which the constant is evaluated (e.g. the function within which the constant is used)
and a [`GlobalId`]. The `GlobalId` is made up of an `Instance` referring to a constant
or static or of an `Instance` of a function and an index into the function's `Promoted` table.

View File

@ -954,9 +954,6 @@ application of these fields based on a variety of attributes when using
`Self="std::iter::Iterator<char>"`. This is needed because `Self` is a
keyword which cannot appear in attributes.
- `direct`: user-specified rather than derived obligation.
- `from_method`: usable both as boolean (whether the flag is present, like
`crate_local`) or matching against a particular method. Currently used
for `try`.
- `from_desugaring`: usable both as boolean (whether the flag is present)
or matching against a particular desugaring. The desugaring is identified
with its variant name in the `DesugaringKind` enum.

View File

@ -120,9 +120,9 @@ even though they should be visible by ordinary scoping rules. An example:
fn do_something<T: Default>(val: T) { // <- New rib in both types and values (1)
// `val` is accessible, as is the helper function
// `T` is accessible
let helper = || { // New rib on `helper` (2) and another on the block (3)
let helper = || { // New rib on the block (2)
// `val` is accessible here
}; // End of (3)
}; // End of (2), new rib on `helper` (3)
// `val` is accessible, `helper` variable shadows `helper` function
fn helper() { // <- New rib in both types and values (4)
// `val` is not accessible here, (4) is not transparent for locals
@ -130,7 +130,7 @@ fn do_something<T: Default>(val: T) { // <- New rib in both types and values (1)
} // End of (4)
let val = T::default(); // New rib (5)
// `val` is the variable, not the parameter here
} // End of (5), (2) and (1)
} // End of (5), (3) and (1)
```
Because the rules for different namespaces are a bit different, each namespace

View File

@ -0,0 +1,12 @@
# Fuchsia notification group
**Github Label:** [O-fuchsia] <br>
**Ping command:** `@rustbot ping fuchsia`
[O-fuchsia]: https://github.com/rust-lang/rust/labels/O-fuchsia
This list will be used to notify [Fuchsia][fuchsia] maintainers
when the compiler or the standard library changes in a way that would
break the Fuchsia integration.
[fuchsia]: ../tests/ecosystem-test-jobs/fuchsia.md

View File

@ -1,43 +0,0 @@
# Which `ParamEnv` do I use?
When needing a [`ParamEnv`][pe] in the compiler there are a few options for obtaining one:
- The correct env is already in scope simply use it (or pass it down the call stack to where you are).
- The [`tcx.param_env(def_id)` query][param_env_query]
- Use [`ParamEnv::new`][param_env_new] to construct an env with an arbitrary set of where clauses. Then call [`traits::normalize_param_env_or_error`][normalize_env_or_error] which will handle normalizing and elaborating all the where clauses in the env for you.
- Creating an empty environment via [`ParamEnv::reveal_all`][env_reveal_all] or [`ParamEnv::empty`][env_empty]
In the large majority of cases a `ParamEnv` when required already exists somewhere in scope or above in the call stack and should be passed down. A non exhaustive list of places where you might find an existing `ParamEnv`:
- During typeck `FnCtxt` has a [`param_env` field][fnctxt_param_env]
- When writing late lints the `LateContext` has a [`param_env` field][latectxt_param_env]
- During well formedness checking the `WfCheckingCtxt` has a [`param_env` field][wfckctxt_param_env]
- The `TypeChecker` used by Mir Typeck has a [`param_env` field][mirtypeck_param_env]
- In the next-gen trait solver all `Goal`s have a [`param_env` field][goal_param_env] specifying what environment to prove the goal in
- When editing an existing [`TypeRelation`][typerelation] if it implements `PredicateEmittingRelation` then a [`param_env` method][typerelation_param_env] will be available.
Using the `param_env` query to obtain an env is generally done at the start of some kind of analysis and then passed everywhere that a `ParamEnv` is required. For example the type checker will create a `ParamEnv` for the item it is type checking and then pass it around everywhere.
Creating an env from an arbitrary set of where clauses is usually unnecessary and should only be done if the environment you need does not correspond to an actual item in the source code (i.e. [`compare_method_predicate_entailment`][method_pred_entailment] as mentioned earlier).
Creating an empty environment via `ParamEnv::empty` is almost always wrong. There are very few places where we actually know that the environment should be empty. One of the only places where we do actually know this is after monomorphization, however the `ParamEnv` there should be constructed via `ParamEnv::reveal_all` instead as at this point we should be able to determine the hidden type of opaque types. Codegen/Post-mono is one of the only places that should be using `ParamEnv::reveal_all`.
An additional piece of complexity here is specifying the `Reveal` (see linked docs for explanation of what reveal does) used for the `ParamEnv`. When constructing a param env using the `param_env` query it will have `Reveal::UserFacing`, if `Reveal::All` is desired then the [`tcx.param_env_reveal_all_normalized`][env_reveal_all_normalized] query can be used instead.
The `ParamEnv` type has a method [`ParamEnv::with_reveal_all_normalized`][with_reveal_all] which converts an existing `ParamEnv` into one with `Reveal::All` specified. Where possible the previously mentioned query should be preferred as it is more efficient.
[param_env_new]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.new
[normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html
[fnctxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env
[latectxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/context/struct.LateContext.html#structfield.param_env
[wfckctxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/wfcheck/struct.WfCheckingCtxt.html#structfield.param_env
[goal_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/canonical/ir/solve/struct.Goal.html#structfield.param_env
[typerelation_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/trait.PredicateEmittingRelation.html#tymethod.param_env
[typerelation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/relate/trait.TypeRelation.html
[mirtypeck_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/type_check/struct.TypeChecker.html#structfield.param_env
[env_reveal_all_normalized]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.param_env_reveal_all_normalized
[with_reveal_all]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.with_reveal_all_normalized
[env_reveal_all]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.reveal_all
[env_empty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.empty
[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html
[param_env_query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env
[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html

View File

@ -1,83 +0,0 @@
# How are `ParamEnv`'s constructed internally?
Creating a [`ParamEnv`][pe] is more complicated than simply using the list of where clauses defined on an item as written by the user. We need to both elaborate supertraits into the env and fully normalize all aliases. This logic is handled by [`traits::normalize_param_env_or_error`][normalize_env_or_error] (even though it does not mention anything about elaboration).
## Elaborating supertraits
When we have a function such as `fn foo<T: Copy>()` we would like to be able to prove `T: Clone` inside of the function as the `Copy` trait has a `Clone` supertrait. Constructing a `ParamEnv` looks at all of the trait bounds in the env and explicitly adds new where clauses to the `ParamEnv` for any supertraits found on the traits.
A concrete example would be the following function:
```rust
trait Trait: SuperTrait {}
trait SuperTrait: SuperSuperTrait {}
// `bar`'s unelaborated `ParamEnv` would be:
// `[T: Sized, T: Copy, T: Trait]`
fn bar<T: Copy + Trait>(a: T) {
requires_impl(a);
}
fn requires_impl<T: Clone + SuperSuperTrait>(a: T) {}
```
If we did not elaborate the env then the `requires_impl` call would fail to typecheck as we would not be able to prove `T: Clone` or `T: SuperSuperTrait`. In practice we elaborate the env which means that `bar`'s `ParamEnv` is actually:
`[T: Sized, T: Copy, T: Clone, T: Trait, T: SuperTrait, T: SuperSuperTrait]`
This allows us to prove `T: Clone` and `T: SuperSuperTrait` when type checking `bar`.
The `Clone` trait has a `Sized` supertrait however we do not end up with two `T: Sized` bounds in the env (one for the supertrait and one for the implicitly added `T: Sized` bound). This is because the elaboration process (implemented via [`util::elaborate`][elaborate]) deduplicates the where clauses to avoid this.
As a side effect this also means that even if no actual elaboration of supertraits takes place, the existing where clauses in the env are _also_ deduplicated. See the following example:
```rust
trait Trait {}
// The unelaborated `ParamEnv` would be:
// `[T: Sized, T: Trait, T: Trait]`
// but after elaboration it would be:
// `[T: Sized, T: Trait]`
fn foo<T: Trait + Trait>() {}
```
The [next-gen trait solver][next-gen-solver] also requires this elaboration to take place.
[elaborate]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/traits/util/fn.elaborate.html
[next-gen-solver]: ../solve/trait-solving.md
## Normalizing all bounds
In the old trait solver the where clauses stored in `ParamEnv` are required to be fully normalized or else the trait solver will not function correctly. A concrete example of needing to normalize the `ParamEnv` is the following:
```rust
trait Trait<T> {
type Assoc;
}
trait Other {
type Bar;
}
impl<T> Other for T {
type Bar = u32;
}
// `foo`'s unnormalized `ParamEnv` would be:
// `[T: Sized, U: Sized, U: Trait<T::Bar>]`
fn foo<T, U>(a: U)
where
U: Trait<<T as Other>::Bar>,
{
requires_impl(a);
}
fn requires_impl<U: Trait<u32>>(_: U) {}
```
As humans we can tell that `<T as Other>::Bar` is equal to `u32` so the trait bound on `U` is equivalent to `U: Trait<u32>`. In practice trying to prove `U: Trait<u32>` in the old solver in this environment would fail as it is unable to determine that `<T as Other>::Bar` is equal to `u32`.
To work around this we normalize `ParamEnv`'s after constructing them so that `foo`'s `ParamEnv` is actually: `[T: Sized, U: Sized, U: Trait<u32>]` which means the trait solver is now able to use the `U: Trait<u32>` in the `ParamEnv` to determine that the trait bound `U: Trait<u32>` holds.
This workaround does not work in all cases as normalizing associated types requires a `ParamEnv` which introduces a bootstrapping problem. We need a normalized `ParamEnv` in order for normalization to give correct results, but we need to normalize to get that `ParamEnv`. Currently we normalize the `ParamEnv` once using the unnormalized param env and it tends to give okay results in practice even though there are some examples where this breaks ([example]).
In the next-gen trait solver the requirement for all where clauses in the `ParamEnv` to be fully normalized is not present and so we do not normalize when constructing `ParamEnv`s.
[example]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e6933265ea3e84eaa47019465739992c
[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html
[normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html

View File

@ -1,18 +0,0 @@
# The `ParamEnv` type
## Summary
The [`ParamEnv`][pe] is used to store information about the environment that we are interacting with the type system from. For example the set of in-scope where-clauses is stored in `ParamEnv` as it differs between each item whereas the list of user written impls is not stored in the `ParamEnv` as this does not change for each item.
This chapter of the dev guide covers:
- A high level summary of what a `ParamEnv` is and what it is used for
- Technical details about what the process of constructing a `ParamEnv` involves
- Guidance about how to acquire a `ParamEnv` when one is required
## Bundling
A useful API on `ParamEnv` is the [`and`][and] method which allows bundling a value with the `ParamEnv`. The `and` method produces a [`ParamEnvAnd<T>`][pea] making it clearer that using the inner value is intended to be done in that specific environment.
[and]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.and
[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html
[pea]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnvAnd.html

View File

@ -1,59 +0,0 @@
# What is a `ParamEnv`?
The type system relies on information in the environment in order for it to function correctly. This information is stored in the [`ParamEnv`][pe] type and it is important to use the correct `ParamEnv` when interacting with the type system.
The information represented by `ParamEnv` is a list of in-scope where-clauses, and a `Reveal` (see linked docs for more information). A `ParamEnv` typically corresponds to a specific item's where clauses, some clauses are not explicitly written bounds and instead are implicitly added in [`predicates_of`][predicates_of] such as `ConstArgHasType` or some implied bounds.
A `ParamEnv` can also be created with arbitrary data that is not derived from a specific item such as in [`compare_method_predicate_entailment`][method_pred_entailment] which creates a hybrid `ParamEnv` consisting of the impl's where clauses and the trait definition's function's where clauses. In most cases `ParamEnv`s are initially created via the [`param_env` query][query] which returns a `ParamEnv` derived from the provided item's where clauses.
If we have a function such as:
```rust
// `foo` would have a `ParamEnv` of:
// `[T: Sized, T: Trait, <T as Trait>::Assoc: Clone]`
fn foo<T: Trait>()
where
<T as Trait>::Assoc: Clone,
{}
```
If we were conceptually inside of `foo` (for example, type-checking or linting it) we would use this `ParamEnv` everywhere that we interact with the type system. This would allow things such as normalization (TODO: write a chapter about normalization and link it), evaluating generic constants, and proving where clauses/goals, to rely on `T` being sized, implementing `Trait`, etc.
A more concrete example:
```rust
// `foo` would have a `ParamEnv` of:
// `[T: Sized, T: Clone]`
fn foo<T: Clone>(a: T) {
// when typechecking `foo` we require all the where clauses on `bar`
// to hold in order for it to be legal to call. This means we have to
// prove `T: Clone`. As we are type checking `foo` we use `foo`'s
// environment when trying to check that `T: Clone` holds.
//
// Trying to prove `T: Clone` with a `ParamEnv` of `[T: Sized, T: Clone]`
// will trivially succeed as bound we want to prove is in our environment.
requires_clone(a);
}
```
Or alternatively an example that would not compile:
```rust
// `foo2` would have a `ParamEnv` of:
// `[T: Sized]`
fn foo2<T>(a: T) {
// When typechecking `foo2` we attempt to prove `T: Clone`.
// As we are type checking `foo2` we use `foo2`'s environment
// when trying to prove `T: Clone`.
//
// Trying to prove `T: Clone` with a `ParamEnv` of `[T: Sized]` will
// fail as there is nothing in the environment telling the trait solver
// that `T` implements `Clone` and there exists no user written impl
// that could apply.
requires_clone(a);
}
```
It's very important to use the correct `ParamEnv` when interacting with the type system as otherwise it can lead to ICEs or things compiling when they shouldn't (or vice versa). See [#82159](https://github.com/rust-lang/rust/pull/82159) and [#82067](https://github.com/rust-lang/rust/pull/82067) as examples of PRs that changed rustc to use the correct param env to avoid ICE. Determining how to acquire the correct `ParamEnv` is explained later in this chapter.
[predicates_of]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/collect/predicates_of/fn.predicates_of.html
[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html
[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html
[query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.param_env

View File

@ -7,7 +7,7 @@ otherwise be printed to stderr.
To get diagnostics from the compiler,
configure [`rustc_interface::Config`] to output diagnostic to a buffer,
and run [`TyCtxt.analysis`].
and run [`rustc_hir_typeck::typeck`] for each item.
```rust
{{#include ../../examples/rustc-interface-getting-diagnostics.rs}}
@ -16,3 +16,4 @@ and run [`TyCtxt.analysis`].
[`rustc_interface`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html
[`rustc_interface::Config`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html
[`TyCtxt.analysis`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/passes/fn.analysis.html
[`rustc_hir_typeck::typeck`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn.typeck.html

View File

@ -0,0 +1,54 @@
# Remarks on perma unstable features
## `rustc_private`
### Overview
The `rustc_private` feature allows external crates to use compiler internals.
### Using `rustc-private` with Official Toolchains
When using the `rustc_private` feature with official Rust toolchains distributed via rustup, you need to install two additional components:
1. **`rustc-dev`**: Provides compiler libraries
2. **`llvm-tools`**: Provides LLVM libraries required for linking
#### Installation Steps
Install both components using rustup:
```text
rustup component add rustc-dev llvm-tools
```
#### Common Error
Without the `llvm-tools` component, you'll encounter linking errors like:
```text
error: linking with `cc` failed: exit status: 1
|
= note: rust-lld: error: unable to find library -lLLVM-{version}
```
### Using `rustc-private` with Custom Toolchains
For custom-built toolchains or environments not using rustup, additional configuration is typically required:
#### Requirements
- LLVM libraries must be available in your system's library search paths
- The LLVM version must match the one used to build your Rust toolchain
#### Troubleshooting Steps
1. **Check LLVM installation**: Verify LLVM is installed and accessible
2. **Configure library paths**: You may need to set environment variables:
```text
export LD_LIBRARY_PATH=/path/to/llvm/lib:$LD_LIBRARY_PATH
```
3. **Check version compatibility**: Ensure your LLVM version is compatible with your Rust toolchain
### Additional Resources
- [GitHub Issue #137421](https://github.com/rust-lang/rust/issues/137421): Explains that `rustc_private` linker failures often occur because `llvm-tools` is not installed

View File

@ -0,0 +1,112 @@
# The `rustdoc` test suite
This page is specifically about the test suite named `rustdoc`.
For other test suites used for testing rustdoc, see [Rustdoc tests](../rustdoc.md#tests).
The `rustdoc` test suite is specifically used to test the HTML output of rustdoc.
This is achieved by means of `htmldocck.py`, a custom checker script that leverages [XPath].
[XPath]: https://en.wikipedia.org/wiki/XPath
## Directives
Directives to htmldocck are similar to those given to `compiletest` in that they take the form of `//@` comments.
In addition to the directives listed here,
`rustdoc` tests also support most
[compiletest directives](../tests/directives.html).
All `PATH`s in directives are relative to the the rustdoc output directory (`build/TARGET/test/rustdoc/TESTNAME`),
so it is conventional to use a `#![crate_name = "foo"]` attribute to avoid
having to write a long crate name multiple times.
To avoid repetion, `-` can be used in any `PATH` argument to re-use the previous `PATH` argument.
All arguments take the form of quoted strings
(both single and double quotes are supported),
with the exception of `COUNT` and the special `-` form of `PATH`.
Directives are assertions that place constraints on the generated HTML.
All directives (except `files`) can be negated by putting a `!` in front of their name.
Similar to shell commands,
directives can extend across multiple lines if their last char is `\`.
In this case, the start of the next line should be `//`, with no `@`.
For example, `//@ !has 'foo/struct.Bar.html'` checks that crate `foo` does not have a page for a struct named `Bar` in the crate root.
### `has`
Usage 1: `//@ has PATH`
Usage 2: `//@ has PATH XPATH PATTERN`
In the first form, `has` checks that a given file exists.
In the second form, `has` is an alias for `matches`,
except `PATTERN` is a whitespace-normalized[^1] string instead of a regex.
### `matches`
Usage: `//@ matches PATH XPATH PATTERN`
Checks that the text of each element selected by `XPATH` in `PATH` matches the python-flavored regex `PATTERN`.
### `matchesraw`
Usage: `//@ matchesraw PATH PATTERN`
Checks that the contents of the file `PATH` matches the regex `PATTERN`.
### `hasraw`
Usage: `//@ hasraw PATH PATTERN`
Same as `matchesraw`, except `PATTERN` is a whitespace-normalized[^1] string instead of a regex.
### `count`
Usage: `//@ count PATH XPATH COUNT`
Checks that there are exactly `COUNT` matches for `XPATH` within the file `PATH`.
### `snapshot`
Usage: `//@ snapshot NAME PATH XPATH`
Creates a snapshot test named NAME.
A snapshot test captures a subtree of the DOM, at the location
determined by the XPath, and compares it to a pre-recorded value
in a file. The file's name is the test's name with the `.rs` extension
replaced with `.NAME.html`, where NAME is the snapshot's name.
htmldocck supports the `--bless` option to accept the current subtree
as expected, saving it to the file determined by the snapshot's name.
compiletest's `--bless` flag is forwarded to htmldocck.
### `has-dir`
Usage: `//@ has-dir PATH`
Checks for the existance of directory `PATH`.
### `files`
Usage: `//@ files PATH ENTRIES`
Checks that the directory `PATH` contains exactly `ENTRIES`.
`ENTRIES` is a python list of strings inside a quoted string,
as if it were to be parsed by `eval`.
(note that the list is actually parsed by `shlex.split`,
so it cannot contain arbitrary python expressions).
Example: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'`
[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space. The files themselves are also whitespace-normalized.
## Limitations
`htmldocck.py` uses the xpath implementation from the standard library.
This leads to several limitations:
* All `XPATH` arguments must start with `//` due to a flaw in the implemention.
* Many XPath features (functions, axies, etc.) are not supported.
* Only well-formed HTML can be parsed (hopefully rustdoc doesn't output mismatched tags).

View File

@ -77,27 +77,27 @@ does is call the `main()` that's in this crate's `lib.rs`, though.)
`doctest.rs`.
* The Markdown renderer is loaded up in `html/markdown.rs`, including functions
for extracting doctests from a given block of Markdown.
* The tests on the structure of rustdoc HTML output are located in `tests/rustdoc`, where
they're handled by the test runner of bootstrap and the supplementary script
`src/etc/htmldocck.py`.
* Frontend CSS and JavaScript are stored in `html/static/`.
## Tests
* All paths in this section are relative to `tests` in the rust-lang/rust repository.
* Tests on search engine and index are located in `rustdoc-js` and `rustdoc-js-std`.
* Tests on search engine and index are located in `tests/rustdoc-js` and `tests/rustdoc-js-std`.
The format is specified
[in the search guide](rustdoc-internals/search.md#testing-the-search-engine).
* Tests on the "UI" of rustdoc (the terminal output it produces when run) are in
`rustdoc-ui`
`tests/rustdoc-ui`
* Tests on the "GUI" of rustdoc (the HTML, JS, and CSS as rendered in a browser)
are in `rustdoc-gui`. These use a [NodeJS tool called
are in `tests/rustdoc-gui`. These use a [NodeJS tool called
browser-UI-test](https://github.com/GuillaumeGomez/browser-UI-test/) that uses
puppeteer to run tests in a headless browser and check rendering and
interactivity.
* Additionally, JavaScript type annotations are written using [TypeScript-flavored JSDoc]
comments and an external d.ts file. The code itself is plain, valid JavaScript; we only
use tsc as a linter.
* The tests on the structure of rustdoc HTML output are located in `tests/rustdoc`,
where they're handled by the test runner of bootstrap and
the supplementary script `src/etc/htmldocck.py`.
[These tests have several extra directives available to them](./rustdoc-internals/rustdoc-test-suite.md).
[TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html

View File

@ -0,0 +1,3 @@
# Cranelift codegen backend tests
TODO: please add some more information to this page.

View File

@ -0,0 +1,3 @@
# GCC codegen backend tests
TODO: please add some more information to this page.

View File

@ -0,0 +1,13 @@
# Codegen backend testing
See also the [Code generation](../../backend/codegen.md) chapter.
In addition to the primary LLVM codegen backend, the rust-lang/rust CI also runs tests of the [cranelift][cg_clif] and [GCC][cg_gcc] codegen backends in certain test jobs.
For more details on the tests involved, see:
- [Cranelift codegen backend tests](./cg_clif.md)
- [GCC codegen backend tests](./cg_gcc.md)
[cg_clif]: https://github.com/rust-lang/rustc_codegen_cranelift
[cg_gcc]: https://github.com/rust-lang/rustc_codegen_gcc

View File

@ -4,6 +4,14 @@
million lines of Rust code.[^loc] It has caught a large number of [regressions]
in the past and was subsequently included in CI.
## What to do if the Fuchsia job breaks?
Please contact the [fuchsia][fuchsia-ping] ping group and ask them for help.
```text
@rustbot ping fuchsia
```
## Building Fuchsia in CI
Fuchsia builds as part of the suite of bors tests that run before a pull request
@ -32,7 +40,7 @@ using your local Rust toolchain.
src/ci/docker/run.sh x86_64-fuchsia
```
See the [Testing with Docker](docker.md) chapter for more details on how to run
See the [Testing with Docker](../docker.md) chapter for more details on how to run
and debug jobs with Docker.
Note that a Fuchsia checkout is *large* as of this writing, a checkout and
@ -162,6 +170,7 @@ rustc book][platform-support].
[`public_configs`]: https://gn.googlesource.com/gn/+/main/docs/reference.md#var_public_configs
[`//build/config:compiler`]: https://cs.opensource.google/fuchsia/fuchsia/+/main:build/config/BUILD.gn;l=121;drc=c26c473bef93b33117ae417893118907a026fec7
[build system]: https://fuchsia.dev/fuchsia-src/development/build/build_system
[fuchsia-ping]: ../../notification-groups/fuchsia.md
[^loc]: As of June 2024, Fuchsia had about 2 million lines of first-party Rust
code and a roughly equal amount of third-party code, as counted by tokei

View File

@ -3,26 +3,7 @@
[Rust for Linux](https://rust-for-linux.com/) (RfL) is an effort for adding
support for the Rust programming language into the Linux kernel.
## Building Rust for Linux in CI
Rust for Linux builds as part of the suite of bors tests that run before a pull
request is merged.
The workflow builds a stage1 sysroot of the Rust compiler, downloads the Linux
kernel, and tries to compile several Rust for Linux drivers and examples using
this sysroot. RfL uses several unstable compiler/language features, therefore
this workflow notifies us if a given compiler change would break it.
If you are worried that a pull request might break the Rust for Linux builder
and want to test it out before submitting it to the bors queue, simply add this
line to your PR description:
> try-job: x86_64-rust-for-linux
Then when you `@bors try` it will pick the job that builds the Rust for Linux
integration.
## What to do in case of failure
## What to do if the Rust for Linux job breaks?
If a PR breaks the Rust for Linux CI job, then:
@ -48,4 +29,23 @@ ping group to ask for help:
@rustbot ping rfl
```
[rfl-ping]: ../notification-groups/rust-for-linux.md
## Building Rust for Linux in CI
Rust for Linux builds as part of the suite of bors tests that run before a pull
request is merged.
The workflow builds a stage1 sysroot of the Rust compiler, downloads the Linux
kernel, and tries to compile several Rust for Linux drivers and examples using
this sysroot. RfL uses several unstable compiler/language features, therefore
this workflow notifies us if a given compiler change would break it.
If you are worried that a pull request might break the Rust for Linux builder
and want to test it out before submitting it to the bors queue, simply add this
line to your PR description:
> try-job: x86_64-rust-for-linux
Then when you `@bors try` it will pick the job that builds the Rust for Linux
integration.
[rfl-ping]: ../../notification-groups/rust-for-linux.md

View File

@ -24,5 +24,5 @@ there aren't any significant regressions.
We have CI jobs that build large open-source Rust projects that are used as
regression tests in CI. Our integration jobs build the following projects:
- [Fuchsia](fuchsia.md)
- [Rust for Linux](rust-for-linux.md)
- [Fuchsia](./ecosystem-test-jobs/fuchsia.md)
- [Rust for Linux](./ecosystem-test-jobs/rust-for-linux.md)

View File

@ -38,7 +38,7 @@ directory, and `x` will essentially run `cargo test` on that package.
Examples:
| Command | Description |
| ----------------------------------------- | ------------------------------------- |
|-------------------------------------------|---------------------------------------|
| `./x test library/std` | Runs tests on `std` only |
| `./x test library/core` | Runs tests on `core` only |
| `./x test compiler/rustc_data_structures` | Runs tests on `rustc_data_structures` |
@ -86,7 +86,7 @@ above.
Examples:
| Command | Description |
| ----------------------- | ------------------------------------------------------------------ |
|-------------------------|--------------------------------------------------------------------|
| `./x fmt --check` | Checks formatting and exits with an error if formatting is needed. |
| `./x fmt` | Runs rustfmt across the entire codebase. |
| `./x test tidy --bless` | First runs rustfmt to format the codebase, then runs tidy checks. |
@ -155,6 +155,10 @@ chapter](ecosystem.md) for more details.
A separate infrastructure is used for testing and tracking performance of the
compiler. See the [Performance testing chapter](perf.md) for more details.
### Codegen backend testing
See [Codegen backend testing](./codegen-backend-tests/intro.md).
## Miscellaneous information
There are some other useful testing-related info at [Misc info](misc.md).

View File

@ -448,6 +448,14 @@ reasons, including:
can alert the developer so they know that the associated issue has been fixed
and can possibly be closed.
This directive takes comma-separated issue numbers as arguments, or `"unknown"`:
- `//@ known-bug: #123, #456` (when the issues are on rust-lang/rust)
- `//@ known-bug: rust-lang/chalk#123456`
(allows arbitrary text before the `#`, which is useful when the issue is on another repo)
- `//@ known-bug: unknown`
(when there is no known issue yet; preferrably open one if it does not already exist)
Do not include [error annotations](#error-annotations) in a test with
`known-bug`. The test should still include other normal directives and
stdout/stderr files.

View File

@ -61,7 +61,7 @@ to be pretty clearly safe and also still retains a very high hit rate
**TODO**: it looks like `pick_candidate_cache` no longer exists. In
general, is this section still accurate at all?
[`ParamEnv`]: ../param_env/param_env_summary.html
[`ParamEnv`]: ../typing_parameter_envs.html
[`tcx`]: ../ty.html
[#18290]: https://github.com/rust-lang/rust/issues/18290
[#22019]: https://github.com/rust-lang/rust/issues/22019

View File

@ -183,7 +183,7 @@ in that list. If so, it is considered satisfied. More precisely, we
want to check whether there is a where-clause obligation that is for
the same trait (or some subtrait) and which can match against the obligation.
[parameter environment]: ../param_env/param_env_summary.html
[parameter environment]: ../typing_parameter_envs.html
Consider this simple example:

View File

@ -40,7 +40,7 @@ We did not always explicitly track the set of bound vars introduced by each `Bin
```
Binder(
fn(&'^1_0 &'^1 T/#0),
&[BoundVariarbleKind::Region(...)],
&[BoundVariableKind::Region(...)],
)
```
This would cause all kinds of issues as the region `'^1_0` refers to a binder at a higher level than the outermost binder i.e. it is an escaping bound var. The `'^1` region (also writeable as `'^0_1`) is also ill formed as the binder it refers to does not introduce a second parameter. Modern day rustc will ICE when constructing this binder due to both of those regions, in the past we would have simply allowed this to work and then ran into issues in other parts of the codebase.

View File

@ -0,0 +1,206 @@
# Typing/Parameter Environments
<!-- toc -->
## Typing Environments
When interacting with the type system there are a few variables to consider that can affect the results of trait solving. The the set of in-scope where clauses, and what phase of the compiler type system operations are being performed in (the [`ParamEnv`][penv] and [`TypingMode`][tmode] structs respectively).
When an environment to perform type system operations in has not yet been created, the [`TypingEnv`][tenv] can be used to bundle all of the external context required into a single type.
Once a context to perform type system operations in has been created (e.g. an [`ObligationCtxt`][ocx] or [`FnCtxt`][fnctxt]) a `TypingEnv` is typically not stored anywhere as only the `TypingMode` is a property of the whole environment, whereas different `ParamEnv`s can be used on a per-goal basis.
[ocx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/struct.ObligationCtxt.html
[fnctxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html
## Parameter Environemnts
### What is a `ParamEnv`
The [`ParamEnv`][penv] is a list of in-scope where-clauses, it typically corresponds to a specific item's where clauses. Some clauses are not explicitly written but are instead are implicitly added in the [`predicates_of`][predicates_of] query, such as `ConstArgHasType` or (some) implied bounds.
In most cases `ParamEnv`s are initially created via the [`param_env` query][query] which returns a `ParamEnv` derived from the provided item's where clauses. A `ParamEnv` can also be created with arbitrary sets of clauses that are not derived from a specific item, such as in [`compare_method_predicate_entailment`][method_pred_entailment] where we create a hybrid `ParamEnv` consisting of the impl's where clauses and the trait definition's function's where clauses.
---
If we have a function such as:
```rust
// `foo` would have a `ParamEnv` of:
// `[T: Sized, T: Trait, <T as Trait>::Assoc: Clone]`
fn foo<T: Trait>()
where
<T as Trait>::Assoc: Clone,
{}
```
If we were conceptually inside of `foo` (for example, type-checking or linting it) we would use this `ParamEnv` everywhere that we interact with the type system. This would allow things such as normalization (TODO: write a chapter about normalization and link it), evaluating generic constants, and proving where clauses/goals, to rely on `T` being sized, implementing `Trait`, etc.
A more concrete example:
```rust
// `foo` would have a `ParamEnv` of:
// `[T: Sized, T: Clone]`
fn foo<T: Clone>(a: T) {
// when typechecking `foo` we require all the where clauses on `requires_clone`
// to hold in order for it to be legal to call. This means we have to
// prove `T: Clone`. As we are type checking `foo` we use `foo`'s
// environment when trying to check that `T: Clone` holds.
//
// Trying to prove `T: Clone` with a `ParamEnv` of `[T: Sized, T: Clone]`
// will trivially succeed as bound we want to prove is in our environment.
requires_clone(a);
}
```
Or alternatively an example that would not compile:
```rust
// `foo2` would have a `ParamEnv` of:
// `[T: Sized]`
fn foo2<T>(a: T) {
// When typechecking `foo2` we attempt to prove `T: Clone`.
// As we are type checking `foo2` we use `foo2`'s environment
// when trying to prove `T: Clone`.
//
// Trying to prove `T: Clone` with a `ParamEnv` of `[T: Sized]` will
// fail as there is nothing in the environment telling the trait solver
// that `T` implements `Clone` and there exists no user written impl
// that could apply.
requires_clone(a);
}
```
[predicates_of]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/collect/predicates_of/fn.predicates_of.html
[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html
[query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.param_env
### Acquiring a `ParamEnv`
Using the wrong [`ParamEnv`][penv] when interacting with the type system can lead to ICEs, illformed programs compiling, or erroing when we shouldn't. See [#82159](https://github.com/rust-lang/rust/pull/82159) and [#82067](https://github.com/rust-lang/rust/pull/82067) as examples of PRs that modified the compiler to use the correct param env and in the process fixed ICEs.
In the large majority of cases, when a `ParamEnv` is required it either already exists somewhere in scope, or above in the call stack and should be passed down. A non exhaustive list of places where you might find an existing `ParamEnv`:
- During typeck `FnCtxt` has a [`param_env` field][fnctxt_param_env]
- When writing late lints the `LateContext` has a [`param_env` field][latectxt_param_env]
- During well formedness checking the `WfCheckingCtxt` has a [`param_env` field][wfckctxt_param_env]
- The `TypeChecker` used for MIR Typeck has a [`param_env` field][mirtypeck_param_env]
- In the next-gen trait solver all `Goal`s have a [`param_env` field][goal_param_env] specifying what environment to prove the goal in
- When editing an existing [`TypeRelation`][typerelation] if it implements [`PredicateEmittingRelation`][predicate_emitting_relation] then a [`param_env` method][typerelation_param_env] will be available.
If you aren't sure if there's a `ParamEnv` in scope somewhere that can be used it can be worth opening a thread in the [`#t-compiler/help`][compiler_help] zulip stream where someone may be able to point out where a `ParamEnv` can be acquired from.
Manually constructing a `ParamEnv` is typically only needed at the start of some kind of top level analysis (e.g. hir typeck or borrow checking). In such cases there are three ways it can be done:
- Calling the [`tcx.param_env(def_id)` query][param_env_query] which returns the environment associated with a given definition.
- Creating an empty environment with [`ParamEnv::empty`][env_empty].
- Using [`ParamEnv::new`][param_env_new] to construct an env with an arbitrary set of where clauses. Then calling [`traits::normalize_param_env_or_error`][normalize_env_or_error] to handle normalizing and elaborating all the where clauses in the env.
Using the `param_env` query is by far the most common way to construct a `ParamEnv` as most of the time the compiler is performing an analysis as part of some specific definition.
Creating an empty environment with `ParamEnv::empty` is typically only done either in codegen (indirectly via [`TypingEnv::fully_monomorphized`][tenv_mono]), or as part of some analysis that do not expect to ever encounter generic parameters (e.g. various parts of coherence/orphan check).
Creating an env from an arbitrary set of where clauses is usually unnecessary and should only be done if the environment you need does not correspond to an actual item in the source code (e.g. [`compare_method_predicate_entailment`][method_pred_entailment]).
[param_env_new]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.new
[normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html
[fnctxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env
[latectxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/context/struct.LateContext.html#structfield.param_env
[wfckctxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/wfcheck/struct.WfCheckingCtxt.html#structfield.param_env
[goal_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/canonical/ir/solve/struct.Goal.html#structfield.param_env
[typerelation_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/trait.PredicateEmittingRelation.html#tymethod.param_env
[typerelation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/relate/trait.TypeRelation.html
[mirtypeck_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/type_check/struct.TypeChecker.html#structfield.param_env
[env_empty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.empty
[param_env_query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env
[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html
[predicate_emitting_relation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/relate/combine/trait.PredicateEmittingRelation.html
[tenv_mono]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypingEnv.html#method.fully_monomorphized
[compiler_help]: https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp
### How are `ParamEnv`s constructed
Creating a [`ParamEnv`][pe] is more complicated than simply using the list of where clauses defined on an item as written by the user. We need to both elaborate supertraits into the env and fully normalize all aliases. This logic is handled by [`traits::normalize_param_env_or_error`][normalize_env_or_error] (even though it does not mention anything about elaboration).
#### Elaborating supertraits
When we have a function such as `fn foo<T: Copy>()` we would like to be able to prove `T: Clone` inside of the function as the `Copy` trait has a `Clone` supertrait. Constructing a `ParamEnv` looks at all of the trait bounds in the env and explicitly adds new where clauses to the `ParamEnv` for any supertraits found on the traits.
A concrete example would be the following function:
```rust
trait Trait: SuperTrait {}
trait SuperTrait: SuperSuperTrait {}
// `bar`'s unelaborated `ParamEnv` would be:
// `[T: Sized, T: Copy, T: Trait]`
fn bar<T: Copy + Trait>(a: T) {
requires_impl(a);
}
fn requires_impl<T: Clone + SuperSuperTrait>(a: T) {}
```
If we did not elaborate the env then the `requires_impl` call would fail to typecheck as we would not be able to prove `T: Clone` or `T: SuperSuperTrait`. In practice we elaborate the env which means that `bar`'s `ParamEnv` is actually:
`[T: Sized, T: Copy, T: Clone, T: Trait, T: SuperTrait, T: SuperSuperTrait]`
This allows us to prove `T: Clone` and `T: SuperSuperTrait` when type checking `bar`.
The `Clone` trait has a `Sized` supertrait however we do not end up with two `T: Sized` bounds in the env (one for the supertrait and one for the implicitly added `T: Sized` bound) as the elaboration process (implemented via [`util::elaborate`][elaborate]) deduplicates where clauses.
A side effect of this is that even if no actual elaboration of supertraits takes place, the existing where clauses in the env are _also_ deduplicated. See the following example:
```rust
trait Trait {}
// The unelaborated `ParamEnv` would be:
// `[T: Sized, T: Trait, T: Trait]`
// but after elaboration it would be:
// `[T: Sized, T: Trait]`
fn foo<T: Trait + Trait>() {}
```
The [next-gen trait solver][next-gen-solver] also requires this elaboration to take place.
[elaborate]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/traits/util/fn.elaborate.html
[next-gen-solver]: ./solve/trait-solving.md
#### Normalizing all bounds
In the old trait solver the where clauses stored in `ParamEnv` are required to be fully normalized as otherwise the trait solver will not function correctly. A concrete example of needing to normalize the `ParamEnv` is the following:
```rust
trait Trait<T> {
type Assoc;
}
trait Other {
type Bar;
}
impl<T> Other for T {
type Bar = u32;
}
// `foo`'s unnormalized `ParamEnv` would be:
// `[T: Sized, U: Sized, U: Trait<T::Bar>]`
fn foo<T, U>(a: U)
where
U: Trait<<T as Other>::Bar>,
{
requires_impl(a);
}
fn requires_impl<U: Trait<u32>>(_: U) {}
```
As humans we can tell that `<T as Other>::Bar` is equal to `u32` so the trait bound on `U` is equivalent to `U: Trait<u32>`. In practice trying to prove `U: Trait<u32>` in the old solver in this environment would fail as it is unable to determine that `<T as Other>::Bar` is equal to `u32`.
To work around this we normalize `ParamEnv`'s after constructing them so that `foo`'s `ParamEnv` is actually: `[T: Sized, U: Sized, U: Trait<u32>]` which means the trait solver is now able to use the `U: Trait<u32>` in the `ParamEnv` to determine that the trait bound `U: Trait<u32>` holds.
This workaround does not work in all cases as normalizing associated types requires a `ParamEnv` which introduces a bootstrapping problem. We need a normalized `ParamEnv` in order for normalization to give correct results, but we need to normalize to get that `ParamEnv`. Currently we normalize the `ParamEnv` once using the unnormalized param env and it tends to give okay results in practice even though there are some examples where this breaks ([example]).
In the next-gen trait solver the requirement for all where clauses in the `ParamEnv` to be fully normalized is not present and so we do not normalize when constructing `ParamEnv`s.
[example]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e6933265ea3e84eaa47019465739992c
[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html
[normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html
## Typing Modes
Depending on what context we are performing type system operations in, different behaviour may be required. For example during coherence there are stronger requirements about when we can consider goals to not hold or when we can consider types to be unequal.
Tracking which "phase" of the compiler type system operations are being performed in is done by the [`TypingMode`][tenv] enum. The documentation on the `TypingMode` enum is quite good so instead of repeating it here verbatim we would recommend reading the API documentation directly.
[penv]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html
[tenv]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/infer_ctxt/enum.TypingMode.html
[tmode]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TypingMode.html