From a50490c5793516acb03c7b27d26177137b3f16f5 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 1 Mar 2024 14:12:11 +0100 Subject: [PATCH 001/192] Use FxIndexMap instead FxHashMap to stabilize iteration order in EffectiveVisibilities. Part of https://github.com/rust-lang/compiler-team/issues/533 --- compiler/rustc_middle/src/middle/privacy.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 500536a9e9ed..46520d69e183 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -2,7 +2,7 @@ //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. use crate::ty::{TyCtxt, Visibility}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::DefKind; use rustc_macros::HashStable; @@ -90,7 +90,7 @@ impl EffectiveVisibility { /// Holds a map of effective visibilities for reachable HIR nodes. #[derive(Clone, Debug)] pub struct EffectiveVisibilities { - map: FxHashMap, + map: FxIndexMap, } impl EffectiveVisibilities { @@ -130,9 +130,8 @@ impl EffectiveVisibilities { eff_vis: &EffectiveVisibility, tcx: TyCtxt<'_>, ) { - use std::collections::hash_map::Entry; match self.map.entry(def_id) { - Entry::Occupied(mut occupied) => { + IndexEntry::Occupied(mut occupied) => { let old_eff_vis = occupied.get_mut(); for l in Level::all_levels() { let vis_at_level = eff_vis.at_level(l); @@ -145,7 +144,7 @@ impl EffectiveVisibilities { } old_eff_vis } - Entry::Vacant(vacant) => vacant.insert(*eff_vis), + IndexEntry::Vacant(vacant) => vacant.insert(*eff_vis), }; } From 95828850b2117cda5c6766f6af862f955a4b8382 Mon Sep 17 00:00:00 2001 From: Jesse Bakker Date: Wed, 13 Mar 2024 17:59:27 +0100 Subject: [PATCH 002/192] Fix panic with impl trait associated types in where clause --- crates/hir-ty/src/lower.rs | 8 ++++++-- crates/hir-ty/src/tests/traits.rs | 34 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index dac20f225971..3e6d81f6ca94 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1107,8 +1107,12 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) = - (type_ref, &self.impl_trait_mode) + if let ( + TypeRef::ImplTrait(bounds), + ImplTraitLoweringState::Param(_) + | ImplTraitLoweringState::Variable(_) + | ImplTraitLoweringState::Disallowed, + ) = (type_ref, &self.impl_trait_mode) { for bound in bounds { predicates.extend( diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index b80cfe18e4cf..ddb6ed82145a 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -1278,6 +1278,40 @@ fn bar() { ); } +#[test] +fn argument_assoc_impl_trait() { + check_infer( + r#" +trait Outer { + type Item; +} + +trait Inner { } + +fn foo>(baz: T) { +} + +impl Outer for usize { + type Item = usize; +} + +impl Inner for usize {} + +fn main() { + foo(2); +} +"#, + expect![[r#" + 85..88 'baz': T + 93..96 '{ }': () + 182..197 '{ foo(2); }': () + 188..191 'foo': fn foo(usize) + 188..194 'foo(2)': () + 192..193 '2': usize + "#]], + ); +} + #[test] fn simple_return_pos_impl_trait() { cov_mark::check!(lower_rpit); From d472fd932b8292479547166ca83b2e2425f57ccc Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 13 Mar 2024 15:47:34 -0700 Subject: [PATCH 003/192] refactor: Rename CargoTask to RustTask in extension --- editors/code/src/run.ts | 10 +++++----- editors/code/src/tasks.ts | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index fc3f1acce544..64a8a945f7f9 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -113,7 +113,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise const args = createArgs(runnable); - const definition: tasks.CargoTaskDefinition = { + const definition: tasks.RustTargetDefinition = { type: tasks.TASK_TYPE, command: args[0], // run, test, etc... args: args.slice(1), @@ -124,7 +124,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() - const cargoTask = await tasks.buildCargoTask( + const task = await tasks.buildRustTask( target, definition, runnable.label, @@ -134,12 +134,12 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise true, ); - cargoTask.presentationOptions.clear = true; + task.presentationOptions.clear = true; // Sadly, this doesn't prevent focus stealing if the terminal is currently // hidden, and will become revealed due to task execution. - cargoTask.presentationOptions.focus = false; + task.presentationOptions.focus = false; - return cargoTask; + return task; } export function createArgs(runnable: ra.Runnable): string[] { diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 1d5ab82aa04b..7525dd0ff77a 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -9,7 +9,7 @@ import { unwrapUndefinable } from "./undefinable"; export const TASK_TYPE = "cargo"; export const TASK_SOURCE = "rust"; -export interface CargoTaskDefinition extends vscode.TaskDefinition { +export interface RustTargetDefinition extends vscode.TaskDefinition { command?: string; args?: string[]; cwd?: string; @@ -17,7 +17,7 @@ export interface CargoTaskDefinition extends vscode.TaskDefinition { overrideCargo?: string; } -class CargoTaskProvider implements vscode.TaskProvider { +class RustTaskProvider implements vscode.TaskProvider { private readonly config: Config; constructor(config: Config) { @@ -42,7 +42,7 @@ class CargoTaskProvider implements vscode.TaskProvider { const tasks: vscode.Task[] = []; for (const workspaceTarget of vscode.workspace.workspaceFolders || []) { for (const def of defs) { - const vscodeTask = await buildCargoTask( + const vscodeTask = await buildRustTask( workspaceTarget, { type: TASK_TYPE, command: def.command }, `cargo ${def.command}`, @@ -63,11 +63,11 @@ class CargoTaskProvider implements vscode.TaskProvider { // we need to inform VSCode how to execute that command by creating // a ShellExecution for it. - const definition = task.definition as CargoTaskDefinition; + const definition = task.definition as RustTargetDefinition; if (definition.type === TASK_TYPE && definition.command) { const args = [definition.command].concat(definition.args ?? []); - return await buildCargoTask( + return await buildRustTask( task.scope, definition, task.name, @@ -81,9 +81,9 @@ class CargoTaskProvider implements vscode.TaskProvider { } } -export async function buildCargoTask( +export async function buildRustTask( scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined, - definition: CargoTaskDefinition, + definition: RustTargetDefinition, name: string, args: string[], problemMatcher: string[], @@ -138,6 +138,6 @@ export async function buildCargoTask( } export function activateTaskProvider(config: Config): vscode.Disposable { - const provider = new CargoTaskProvider(config); + const provider = new RustTaskProvider(config); return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); } From 2e109c7da8fb5b4adc809f30035472512e7ac7cd Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 13 Mar 2024 16:46:57 -0700 Subject: [PATCH 004/192] refactor: Use a single CLI args array rather than a separate subcommand field --- editors/code/src/run.ts | 4 +--- editors/code/src/tasks.ts | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 64a8a945f7f9..b6c730a4cd6a 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -115,8 +115,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise const definition: tasks.RustTargetDefinition = { type: tasks.TASK_TYPE, - command: args[0], // run, test, etc... - args: args.slice(1), + args, cwd: runnable.args.workspaceRoot || ".", env: prepareEnv(runnable, config.runnablesExtraEnv), overrideCargo: runnable.args.overrideCargo, @@ -128,7 +127,6 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise target, definition, runnable.label, - args, config.problemMatcher, config.cargoRunner, true, diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 7525dd0ff77a..39684b4165ff 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -10,8 +10,7 @@ export const TASK_TYPE = "cargo"; export const TASK_SOURCE = "rust"; export interface RustTargetDefinition extends vscode.TaskDefinition { - command?: string; - args?: string[]; + args: string[]; cwd?: string; env?: { [key: string]: string }; overrideCargo?: string; @@ -44,9 +43,8 @@ class RustTaskProvider implements vscode.TaskProvider { for (const def of defs) { const vscodeTask = await buildRustTask( workspaceTarget, - { type: TASK_TYPE, command: def.command }, + { type: TASK_TYPE, args: [def.command] }, `cargo ${def.command}`, - [def.command], this.config.problemMatcher, this.config.cargoRunner, ); @@ -65,13 +63,11 @@ class RustTaskProvider implements vscode.TaskProvider { const definition = task.definition as RustTargetDefinition; - if (definition.type === TASK_TYPE && definition.command) { - const args = [definition.command].concat(definition.args ?? []); + if (definition.type === TASK_TYPE) { return await buildRustTask( task.scope, definition, task.name, - args, this.config.problemMatcher, this.config.cargoRunner, ); @@ -85,7 +81,6 @@ export async function buildRustTask( scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined, definition: RustTargetDefinition, name: string, - args: string[], problemMatcher: string[], customRunner?: string, throwOnError: boolean = false, @@ -95,7 +90,12 @@ export async function buildRustTask( if (customRunner) { const runnerCommand = `${customRunner}.buildShellExecution`; try { - const runnerArgs = { kind: TASK_TYPE, args, cwd: definition.cwd, env: definition.env }; + const runnerArgs = { + kind: TASK_TYPE, + args: definition.args, + cwd: definition.cwd, + env: definition.env, + }; const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs); if (customExec) { if (customExec instanceof vscode.ShellExecution) { @@ -119,7 +119,7 @@ export async function buildRustTask( const cargoPath = await toolchain.cargoPath(); const cargoCommand = overrideCargo?.split(" ") ?? [cargoPath]; - const fullCommand = [...cargoCommand, ...args]; + const fullCommand = [...cargoCommand, ...definition.args]; const processName = unwrapUndefinable(fullCommand[0]); exec = new vscode.ProcessExecution(processName, fullCommand.slice(1), definition); From 4422a90b1106065146d0f3f6d0aa86c212753c10 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 13 Mar 2024 17:13:26 -0700 Subject: [PATCH 005/192] refactor: Store the CLI command directly in RustTargetDefinition --- editors/code/src/run.ts | 14 +++++++++++++- editors/code/src/tasks.ts | 18 +++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index b6c730a4cd6a..02ccbb6956a2 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -2,6 +2,7 @@ import * as vscode from "vscode"; import type * as lc from "vscode-languageclient"; import * as ra from "./lsp_ext"; import * as tasks from "./tasks"; +import * as toolchain from "./toolchain"; import type { CtxInit } from "./ctx"; import { makeDebugConfig } from "./debug"; @@ -111,10 +112,21 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise throw `Unexpected runnable kind: ${runnable.kind}`; } - const args = createArgs(runnable); + let program: string; + let args = createArgs(runnable); + if (runnable.args.overrideCargo) { + // Split on spaces to allow overrides like "wrapper cargo". + const cargoParts = runnable.args.overrideCargo.split(" "); + + program = unwrapUndefinable(cargoParts[0]); + args = [...cargoParts.slice(1), ...args]; + } else { + program = await toolchain.cargoPath(); + } const definition: tasks.RustTargetDefinition = { type: tasks.TASK_TYPE, + program, args, cwd: runnable.args.workspaceRoot || ".", env: prepareEnv(runnable, config.runnablesExtraEnv), diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 39684b4165ff..89abb37b0eb4 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -2,7 +2,6 @@ import * as vscode from "vscode"; import * as toolchain from "./toolchain"; import type { Config } from "./config"; import { log } from "./util"; -import { unwrapUndefinable } from "./undefinable"; // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and // our configuration should be compatible with it so use the same key. @@ -10,10 +9,10 @@ export const TASK_TYPE = "cargo"; export const TASK_SOURCE = "rust"; export interface RustTargetDefinition extends vscode.TaskDefinition { + program: string; args: string[]; cwd?: string; env?: { [key: string]: string }; - overrideCargo?: string; } class RustTaskProvider implements vscode.TaskProvider { @@ -38,12 +37,14 @@ class RustTaskProvider implements vscode.TaskProvider { { command: "run", group: undefined }, ]; + const cargoPath = await toolchain.cargoPath(); + const tasks: vscode.Task[] = []; for (const workspaceTarget of vscode.workspace.workspaceFolders || []) { for (const def of defs) { const vscodeTask = await buildRustTask( workspaceTarget, - { type: TASK_TYPE, args: [def.command] }, + { type: TASK_TYPE, program: cargoPath, args: [def.command] }, `cargo ${def.command}`, this.config.problemMatcher, this.config.cargoRunner, @@ -113,16 +114,7 @@ export async function buildRustTask( } if (!exec) { - // Check whether we must use a user-defined substitute for cargo. - // Split on spaces to allow overrides like "wrapper cargo". - const overrideCargo = definition.overrideCargo ?? definition.overrideCargo; - const cargoPath = await toolchain.cargoPath(); - const cargoCommand = overrideCargo?.split(" ") ?? [cargoPath]; - - const fullCommand = [...cargoCommand, ...definition.args]; - - const processName = unwrapUndefinable(fullCommand[0]); - exec = new vscode.ProcessExecution(processName, fullCommand.slice(1), definition); + exec = new vscode.ProcessExecution(definition.program, definition.args, definition); } return new vscode.Task( From d2aba91a0c58111b9a5df1e2a1f26b8caf45be2e Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sat, 16 Mar 2024 03:31:12 +0900 Subject: [PATCH 006/192] feat: Implement ATPIT --- crates/hir-ty/src/chalk_db.rs | 13 ++ crates/hir-ty/src/chalk_ext.rs | 14 ++ crates/hir-ty/src/db.rs | 16 +-- crates/hir-ty/src/display.rs | 28 ++++ crates/hir-ty/src/infer.rs | 151 +++++++++++++++++++--- crates/hir-ty/src/infer/coerce.rs | 17 +++ crates/hir-ty/src/infer/path.rs | 6 +- crates/hir-ty/src/infer/unify.rs | 8 +- crates/hir-ty/src/layout.rs | 3 + crates/hir-ty/src/lib.rs | 13 +- crates/hir-ty/src/lower.rs | 73 ++++++++--- crates/hir-ty/src/mir/monomorphization.rs | 3 + crates/hir-ty/src/tests/traits.rs | 75 +++++++++++ 13 files changed, 365 insertions(+), 55 deletions(-) diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index e678a2fee132..46612242b090 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { }; chalk_ir::Binders::new(binders, bound) } + crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = self + .db + .type_alias_impl_traits(alias) + .expect("impl trait id without impl traits"); + let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); + let data = &datas.impl_traits[idx]; + let bound = OpaqueTyDatumBound { + bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + chalk_ir::Binders::new(binders, bound) + } crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { if let Some((future_trait, future_output)) = self .db diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 795a5996912b..d1aebeff261c 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -268,6 +268,13 @@ impl TyExt for Ty { data.substitute(Interner, &subst).into_value_and_skipped_binders().0 }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &subst).into_value_and_skipped_binders().0 + }) + } } } TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { @@ -280,6 +287,13 @@ impl TyExt for Ty { data.substitute(Interner, &opaque_ty.substitution) }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &opaque_ty.substitution) + }) + } // It always has an parameter for Future::Output type. ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), }; diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 28c497989fe9..90bf46b5056c 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -11,7 +11,7 @@ use base_db::{ use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, - LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId, + LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -23,9 +23,9 @@ use crate::{ layout::{Layout, LayoutError}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, - Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, - Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, - TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, + Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits, + InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment, + TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast { fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; #[salsa::invoke(crate::lower::return_type_impl_traits)] - fn return_type_impl_traits( - &self, - def: FunctionId, - ) -> Option>>; + fn return_type_impl_traits(&self, def: FunctionId) -> Option>>; + + #[salsa::invoke(crate::lower::type_alias_impl_traits)] + fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option>>; #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 20964f5acbd0..269db57bc347 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1063,6 +1063,20 @@ impl HirDisplay for Ty { )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, ¶meters); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => { let future_trait = db .lang_item(body.module(db.upcast()).krate(), LangItem::Future) @@ -1228,6 +1242,20 @@ impl HirDisplay for Ty { SizedByDefault::Sized { anchor: krate }, )?; } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, &opaque_ty.substitution); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 34ba17f145e0..8847f7e24f6e 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -25,8 +25,11 @@ pub(crate) mod unify; use std::{convert::identity, iter, ops::Index}; use chalk_ir::{ - cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, - Scalar, TyKind, TypeFlags, Variance, + cast::Cast, + fold::TypeFoldable, + interner::HasInterner, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance, }; use either::Either; use hir_def::{ @@ -53,14 +56,14 @@ use triomphe::Arc; use crate::{ db::HirDatabase, fold_tys, - infer::coerce::CoerceMany, + infer::{coerce::CoerceMany, unify::InferenceTable}, lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, traits::FnTrait, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, + ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, }; // This lint has a false positive here. See the link below for details. @@ -422,7 +425,7 @@ pub struct InferenceResult { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub type_of_pat: ArenaMap, pub type_of_binding: ArenaMap, - pub type_of_rpit: ArenaMap, + pub type_of_rpit: ArenaMap, /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. pub type_of_for_iterator: FxHashMap, type_mismatches: FxHashMap, @@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> { } fn collect_const(&mut self, data: &ConstData) { - self.return_ty = self.make_ty(&data.type_ref); + let return_ty = self.make_ty(&data.type_ref); + + // Constants might be associated items that define ATPITs. + self.insert_atpit_coercion_table(iter::once(&return_ty)); + + self.return_ty = return_ty; } fn collect_static(&mut self, data: &StaticData) { @@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> { self.write_binding_ty(self_param, ty); } } + let mut params_and_ret_tys = Vec::new(); for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); self.infer_top_pat(*pat, &ty); + params_and_ret_tys.push(ty); } let return_ty = &*data.ret_type; @@ -801,8 +811,11 @@ impl<'a> InferenceContext<'a> { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) { // RPIT opaque types use substitution of their parent function. let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); - let result = - self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders); + let result = self.insert_inference_vars_for_impl_trait( + return_ty, + rpits.clone(), + fn_placeholders, + ); let rpits = rpits.skip_binders(); for (id, _) in rpits.impl_traits.iter() { if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { @@ -817,13 +830,19 @@ impl<'a> InferenceContext<'a> { self.return_ty = self.normalize_associated_types_in(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); + + // Functions might be associated items that define ATPITs. + // To define an ATPITs, that ATPIT must appear in the function's signitures. + // So, it suffices to check for params and return types. + params_and_ret_tys.push(self.return_ty.clone()); + self.insert_atpit_coercion_table(params_and_ret_tys.iter()); } - fn insert_inference_vars_for_rpit( + fn insert_inference_vars_for_impl_trait( &mut self, t: T, - rpits: Arc>, - fn_placeholders: Substitution, + rpits: Arc>, + placeholders: Substitution, ) -> T where T: crate::HasInterner + crate::TypeFoldable, @@ -837,6 +856,7 @@ impl<'a> InferenceContext<'a> { }; let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, + ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx, _ => unreachable!(), }; let bounds = @@ -844,15 +864,14 @@ impl<'a> InferenceContext<'a> { let var = self.table.new_type_var(); let var_subst = Substitution::from1(Interner, var.clone()); for bound in bounds { - let predicate = - bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders); + let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders); let (var_predicate, binders) = predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders(); always!(binders.is_empty(Interner)); // quantified where clauses not yet handled - let var_predicate = self.insert_inference_vars_for_rpit( + let var_predicate = self.insert_inference_vars_for_impl_trait( var_predicate, rpits.clone(), - fn_placeholders.clone(), + placeholders.clone(), ); self.push_obligation(var_predicate.cast(Interner)); } @@ -863,6 +882,106 @@ impl<'a> InferenceContext<'a> { ) } + /// The coercion of a non-inference var into an opaque type should fail, + /// but not in the defining sites of the ATPITs. + /// In such cases, we insert an proxy inference var for each ATPIT, + /// and coerce into it instead of ATPIT itself. + /// + /// The inference var stretagy is effective because; + /// + /// - It can still unify types that coerced into ATPIT + /// - We are pushing `impl Trait` bounds into it + /// + /// This function inserts a map that maps the opaque type to that proxy inference var. + fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator) { + struct OpaqueTyCollector<'a, 'b> { + table: &'b mut InferenceTable<'a>, + opaque_tys: FxHashMap, + } + + impl<'a, 'b> TypeVisitor for OpaqueTyCollector<'a, 'b> { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty( + &mut self, + ty: &chalk_ir::Ty, + outer_binder: DebruijnIndex, + ) -> std::ops::ControlFlow { + let ty = self.table.resolve_ty_shallow(ty); + + if let TyKind::OpaqueType(id, _) = ty.kind(Interner) { + self.opaque_tys.insert(*id, ty.clone()); + } + + ty.super_visit_with(self, outer_binder) + } + } + + // Early return if this is not happening inside the impl block + let impl_id = if let Some(impl_id) = self.resolver.impl_def() { + impl_id + } else { + return; + }; + + let assoc_tys: FxHashSet<_> = self + .db + .impl_data(impl_id) + .items + .iter() + .filter_map(|item| match item { + AssocItemId::TypeAliasId(alias) => Some(*alias), + _ => None, + }) + .collect(); + if assoc_tys.is_empty() { + return; + } + + let mut collector = + OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() }; + for ty in tys { + ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); + } + let atpit_coercion_table: FxHashMap<_, _> = collector + .opaque_tys + .into_iter() + .filter_map(|(opaque_ty_id, ty)| { + if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) = + self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + { + if assoc_tys.contains(&alias_id) { + let atpits = self + .db + .type_alias_impl_traits(alias_id) + .expect("Marked as ATPIT but no impl traits!"); + let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id); + let ty = self.insert_inference_vars_for_impl_trait( + ty, + atpits, + alias_placeholders, + ); + return Some((opaque_ty_id, ty)); + } + } + + None + }) + .collect(); + + if !atpit_coercion_table.is_empty() { + self.table.atpit_coercion_table = Some(atpit_coercion_table); + } + } + fn infer_body(&mut self) { match self.return_coercion { Some(_) => self.infer_return(self.body.body_expr), diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index ff6de61ba649..fba37dbcff42 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -276,6 +276,23 @@ impl InferenceTable<'_> { return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); } + // If we are coercing into an ATPIT, coerce into its proxy inference var, instead. + let mut to_ty = to_ty; + let mut _to = None; + if let Some(atpit_table) = &self.atpit_coercion_table { + if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) { + if !matches!( + from_ty.kind(Interner), + TyKind::InferenceVar(..) | TyKind::OpaqueType(..) + ) { + if let Some(ty) = atpit_table.get(opaque_ty_id) { + _to = Some(ty.clone()); + to_ty = _to.as_ref().unwrap(); + } + } + } + } + // Consider coercing the subtype to a DST if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { return Ok(ret); diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 8f537bb448b9..44b35b2ebbc1 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -15,11 +15,11 @@ use crate::{ method_resolution::{self, VisibleFromModule}, to_chalk_trait_id, utils::generics, - InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - ValueTyDefId, + InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + TyKind, ValueTyDefId, }; -use super::{ExprOrPatId, InferenceContext, TraitRef}; +use super::{ExprOrPatId, InferenceContext}; impl InferenceContext<'_> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index be7547f9bae5..93fd54455b95 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -10,6 +10,7 @@ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; use hir_expand::name; +use rustc_hash::FxHashMap; use smallvec::SmallVec; use triomphe::Arc; @@ -18,8 +19,9 @@ use crate::{ consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, - InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, + Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, + WhereClause, }; impl InferenceContext<'_> { @@ -239,6 +241,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc, + pub(crate) atpit_coercion_table: Option>, var_unification_table: ChalkInferenceTable, type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, @@ -258,6 +261,7 @@ impl<'a> InferenceTable<'a> { InferenceTable { db, trait_env, + atpit_coercion_table: None, var_unification_table: ChalkInferenceTable::new(), type_variable_table: SmallVec::new(), pending_obligations: Vec::new(), diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 9655981cc9cc..dd949e26c2ab 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -389,6 +389,9 @@ pub fn layout_of_ty_query( let infer = db.infer(func.into()); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + return Err(LayoutError::NotImplemented); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index ec97bdc2c434..a7affc5f924b 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -584,24 +584,25 @@ impl TypeFoldable for CallableSig { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ImplTraitId { - ReturnTypeImplTrait(hir_def::FunctionId, RpitId), + ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), + AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } impl_intern_value_trivial!(ImplTraitId); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTraits { - pub(crate) impl_traits: Arena, +pub struct ImplTraits { + pub(crate) impl_traits: Arena, } -has_interner!(ReturnTypeImplTraits); +has_interner!(ImplTraits); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTrait { +pub struct ImplTrait { pub(crate) bounds: Binders>, } -pub type RpitId = Idx; +pub type ImplTraitIdx = Idx; pub fn static_lifetime() -> Lifetime { LifetimeData::Static.intern(Interner) diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index dac20f225971..d65fd4a71cb2 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -61,9 +61,9 @@ use crate::{ InTypeConstIdMetadata, }, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, - FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy, - QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, - Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, + FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, ParamKind, + PolyFnSig, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, }; #[derive(Debug)] @@ -76,7 +76,7 @@ enum ImplTraitLoweringState { /// we're grouping the mutable data (the counter and this field) together /// with the immutable context (the references to the DB and resolver). /// Splitting this up would be a possible fix. - Opaque(RefCell>), + Opaque(RefCell>), Param(Cell), Variable(Cell), Disallowed, @@ -301,15 +301,22 @@ impl<'a> TyLoweringContext<'a> { TypeRef::ImplTrait(bounds) => { match &self.impl_trait_mode { ImplTraitLoweringState::Opaque(opaque_type_data) => { - let func = match self.resolver.generic_def() { - Some(GenericDefId::FunctionId(f)) => f, - _ => panic!("opaque impl trait lowering in non-function"), + let (origin, krate) = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(f)) => { + (Either::Left(f), f.krate(self.db.upcast())) + } + Some(GenericDefId::TypeAliasId(a)) => { + (Either::Right(a), a.krate(self.db.upcast())) + } + _ => panic!( + "opaque impl trait lowering must be in function or type alias" + ), }; // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = opaque_type_data.borrow_mut().alloc(ReturnTypeImplTrait { + let idx = opaque_type_data.borrow_mut().alloc(ImplTrait { bounds: crate::make_single_type_binders(Vec::new()), }); // We don't want to lower the bounds inside the binders @@ -323,13 +330,17 @@ impl<'a> TyLoweringContext<'a> { // away instead of two. let actual_opaque_type_data = self .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { - ctx.lower_impl_trait(bounds, func) + ctx.lower_impl_trait(bounds, krate) }); opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data; - let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx); + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, idx), + |a| ImplTraitId::AssociatedTypeImplTrait(a, idx), + ); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - let generics = generics(self.db.upcast(), func.into()); + let generics = + generics(self.db.upcast(), origin.either(|f| f.into(), |a| a.into())); let parameters = generics.bound_vars_subst(self.db, self.in_binders); TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) } @@ -1270,11 +1281,7 @@ impl<'a> TyLoweringContext<'a> { } } - fn lower_impl_trait( - &self, - bounds: &[Interned], - func: FunctionId, - ) -> ReturnTypeImplTrait { + fn lower_impl_trait(&self, bounds: &[Interned], krate: CrateId) -> ImplTrait { cov_mark::hit!(lower_rpit); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { @@ -1284,7 +1291,6 @@ impl<'a> TyLoweringContext<'a> { .collect(); if !ctx.unsized_types.borrow().contains(&self_ty) { - let krate = func.krate(ctx.db.upcast()); let sized_trait = ctx .db .lang_item(krate, LangItem::Sized) @@ -1301,7 +1307,7 @@ impl<'a> TyLoweringContext<'a> { } predicates }); - ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) } + ImplTrait { bounds: crate::make_single_type_binders(predicates) } } } @@ -1869,6 +1875,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders { let generics = generics(db.upcast(), t.into()); let resolver = t.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, t.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); let type_alias_data = db.type_alias_data(t); if type_alias_data.is_extern { @@ -2029,7 +2036,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, def: hir_def::FunctionId, -) -> Option>> { +) -> Option>> { // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe let data = db.function_data(def); let resolver = def.resolver(db.upcast()); @@ -2038,7 +2045,7 @@ pub(crate) fn return_type_impl_traits( .with_type_param_mode(ParamLoweringMode::Variable); let _ret = ctx_ret.lower_ty(&data.ret_type); let generics = generics(db.upcast(), def.into()); - let return_type_impl_traits = ReturnTypeImplTraits { + let return_type_impl_traits = ImplTraits { impl_traits: match ctx_ret.impl_trait_mode { ImplTraitLoweringState::Opaque(x) => x.into_inner(), _ => unreachable!(), @@ -2051,6 +2058,32 @@ pub(crate) fn return_type_impl_traits( } } +pub(crate) fn type_alias_impl_traits( + db: &dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option>> { + let data = db.type_alias_data(def); + let resolver = def.resolver(db.upcast()); + let ctx = TyLoweringContext::new(db, &resolver, def.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); + if let Some(type_ref) = &data.type_ref { + let _ty = ctx.lower_ty(type_ref); + } + let generics = generics(db.upcast(), def.into()); + let type_alias_impl_traits = ImplTraits { + impl_traits: match ctx.impl_trait_mode { + ImplTraitLoweringState::Opaque(x) => x.into_inner(), + _ => unreachable!(), + }, + }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits))) + } +} + pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability { match m { hir_def::type_ref::Mutability::Shared => Mutability::Not, diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index d2e413f0a33a..12ad67cdc45b 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -82,6 +82,9 @@ impl FallibleTypeFolder for Filler<'_> { }; filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + not_supported!("associated type impl trait"); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { not_supported!("async block impl trait"); } diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index b80cfe18e4cf..83bb90b6178f 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4655,3 +4655,78 @@ fn f() { "#, ); } + +#[test] +fn associated_type_impl_trait() { + check_types( + r#" +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Self::Item; +} +struct S2; +impl Bar for S2 { + type Item = impl Foo; + fn bar(&self) -> Self::Item { + S1 + } +} + +fn test() { + let x = S2.bar(); + //^ impl Foo + ?Sized +} + "#, + ); +} + +#[test] +fn associated_type_impl_traits_complex() { + check_types( + r#" +struct Unary(T); +struct Binary(T, U); + +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Unary; +} +struct S2; +impl Bar for S2 { + type Item = Unary; + fn bar(&self) -> Unary<::Item> { + Unary(Unary(S1)) + } +} + +trait Baz { + type Target1; + type Target2; + fn baz(&self) -> Binary; +} +struct S3; +impl Baz for S3 { + type Target1 = impl Foo; + type Target2 = Unary; + fn baz(&self) -> Binary { + Binary(S1, Unary(S2)) + } +} + +fn test() { + let x = S3.baz(); + //^ Binary> + let y = x.1.0.bar(); + //^ Unary> +} + "#, + ); +} From fc53c59388ea319a37cc599b1cdeef6a0f4f5ef1 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sat, 16 Mar 2024 03:53:55 +0900 Subject: [PATCH 007/192] fix: typo --- crates/hir-ty/src/infer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 8847f7e24f6e..29e127994516 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -832,7 +832,7 @@ impl<'a> InferenceContext<'a> { self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); // Functions might be associated items that define ATPITs. - // To define an ATPITs, that ATPIT must appear in the function's signitures. + // To define an ATPITs, that ATPIT must appear in the function's signatures. // So, it suffices to check for params and return types. params_and_ret_tys.push(self.return_ty.clone()); self.insert_atpit_coercion_table(params_and_ret_tys.iter()); From 351890d682241e4eb9b8a63cddfb22e90567b870 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 15 Mar 2024 17:49:23 -0700 Subject: [PATCH 008/192] rustdoc: clean up formatting --- src/librustdoc/html/static/js/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 875ebe2fc90d..cbfa503f2606 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -426,7 +426,7 @@ function initSearch(rawSearchIndex) { return c === "," || c === "="; } -/** + /** * Returns `true` if the given `c` character is a path separator. For example * `:` in `a::b` or a whitespace in `a b`. * From 5b44bfda7fc62b2874400e613672aefe5b49aaaa Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 16 Mar 2024 17:50:44 -0700 Subject: [PATCH 009/192] rustdoc-search: shard the search result descriptions The descriptions are, on almost all crates[^1], the majority of the size of the search index, even though they aren't really used for searching. This makes it relatively easy to separate them into their own files. This commit also bumps us to ES8. Out of the browsers we support, all of them support async functions according to caniuse. https://caniuse.com/async-functions [^1]: , a crate with 44MiB of pure names and no descriptions for them, is an outlier and should not be counted. --- .../docker/host-x86_64/mingw-check/Dockerfile | 2 +- src/librustdoc/html/render/mod.rs | 33 +- src/librustdoc/html/render/search_index.rs | 101 ++++- src/librustdoc/html/render/write_shared.rs | 31 +- src/librustdoc/html/static/.eslintrc.js | 2 +- src/librustdoc/html/static/js/main.js | 28 +- src/librustdoc/html/static/js/search.js | 346 ++++++++++-------- src/librustdoc/html/static/js/storage.js | 4 +- src/tools/rustdoc-js/.eslintrc.js | 2 +- src/tools/rustdoc-js/tester.js | 104 ++++-- tests/rustdoc/search-index-summaries.rs | 2 +- 11 files changed, 427 insertions(+), 228 deletions(-) diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 30d3a52d82b8..de8db8ee0349 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -56,7 +56,7 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ /scripts/validate-error-codes.sh && \ reuse --include-submodules lint && \ # Runs checks to ensure that there are no ES5 issues in our JS code. - es-check es6 ../src/librustdoc/html/static/js/*.js && \ + es-check es8 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6c5040414bce..c1a7593c26f0 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -184,40 +184,15 @@ pub(crate) enum RenderTypeId { impl RenderTypeId { pub fn write_to_string(&self, string: &mut String) { - // (sign, value) - let (sign, id): (bool, u32) = match &self { + let id: i32 = match &self { // 0 is a sentinel, everything else is one-indexed // concrete type - RenderTypeId::Index(idx) if *idx >= 0 => (false, (idx + 1isize).try_into().unwrap()), + RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(), // generic type parameter - RenderTypeId::Index(idx) => (true, (-*idx).try_into().unwrap()), + RenderTypeId::Index(idx) => (*idx).try_into().unwrap(), _ => panic!("must convert render types to indexes before serializing"), }; - // zig-zag encoding - let value: u32 = (id << 1) | (if sign { 1 } else { 0 }); - // Self-terminating hex use capital letters for everything but the - // least significant digit, which is lowercase. For example, decimal 17 - // would be `` Aa `` if zig-zag encoding weren't used. - // - // Zig-zag encoding, however, stores the sign bit as the last bit. - // This means, in the last hexit, 1 is actually `c`, -1 is `b` - // (`a` is the imaginary -0), and, because all the bits are shifted - // by one, `` A` `` is actually 8 and `` Aa `` is -8. - // - // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html - // describes the encoding in more detail. - let mut shift: u32 = 28; - let mut mask: u32 = 0xF0_00_00_00; - while shift < 32 { - let hexit = (value & mask) >> shift; - if hexit != 0 || shift == 0 { - let hex = - char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); - string.push(hex); - } - shift = shift.wrapping_sub(4); - mask = mask >> 4; - } + search_index::write_vlqhex_to_string(id, string); } } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index f153a9083291..34a4a89aa7bf 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -17,12 +17,25 @@ use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId}; +/// The serialized search description sharded version +/// +/// The `index` is a JSON-encoded list of names and other information. +/// +/// The desc has newlined descriptions, split up by size into 1MiB shards. +/// For example, `(4, "foo\nbar\nbaz\nquux")`. +pub(crate) struct SerializedSearchIndex { + pub(crate) index: String, + pub(crate) desc: Vec<(usize, String)>, +} + +const DESC_INDEX_SHARD_LEN: usize = 1024 * 1024; + /// Builds the search index from the collected metadata pub(crate) fn build_index<'tcx>( krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>, -) -> String { +) -> SerializedSearchIndex { let mut itemid_to_pathid = FxHashMap::default(); let mut primitives = FxHashMap::default(); let mut associated_types = FxHashMap::default(); @@ -318,7 +331,6 @@ pub(crate) fn build_index<'tcx>( .collect::>(); struct CrateData<'a> { - doc: String, items: Vec<&'a IndexItem>, paths: Vec<(ItemType, Vec)>, // The String is alias name and the vec is the list of the elements with this alias. @@ -327,6 +339,9 @@ pub(crate) fn build_index<'tcx>( aliases: &'a BTreeMap>, // Used when a type has more than one impl with an associated item with the same name. associated_item_disambiguators: &'a Vec<(usize, String)>, + // A list of shard lengths encoded as vlqhex. See the comment in write_vlqhex_to_string + // for information on the format. + descindex: String, } struct Paths { @@ -408,7 +423,6 @@ pub(crate) fn build_index<'tcx>( let mut names = Vec::with_capacity(self.items.len()); let mut types = String::with_capacity(self.items.len()); let mut full_paths = Vec::with_capacity(self.items.len()); - let mut descriptions = Vec::with_capacity(self.items.len()); let mut parents = Vec::with_capacity(self.items.len()); let mut functions = String::with_capacity(self.items.len()); let mut deprecated = Vec::with_capacity(self.items.len()); @@ -431,7 +445,6 @@ pub(crate) fn build_index<'tcx>( parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0)); names.push(item.name.as_str()); - descriptions.push(&item.desc); if !item.path.is_empty() { full_paths.push((index, &item.path)); @@ -454,14 +467,12 @@ pub(crate) fn build_index<'tcx>( let has_aliases = !self.aliases.is_empty(); let mut crate_data = serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?; - crate_data.serialize_field("doc", &self.doc)?; crate_data.serialize_field("t", &types)?; crate_data.serialize_field("n", &names)?; - // Serialize as an array of item indices and full paths crate_data.serialize_field("q", &full_paths)?; - crate_data.serialize_field("d", &descriptions)?; crate_data.serialize_field("i", &parents)?; crate_data.serialize_field("f", &functions)?; + crate_data.serialize_field("D", &self.descindex)?; crate_data.serialize_field("c", &deprecated)?; crate_data.serialize_field("p", &paths)?; crate_data.serialize_field("b", &self.associated_item_disambiguators)?; @@ -472,16 +483,46 @@ pub(crate) fn build_index<'tcx>( } } - // Collect the index into a string - format!( + let desc = { + let mut result = Vec::new(); + let mut set = String::new(); + let mut len: usize = 0; + for desc in std::iter::once(&crate_doc).chain(crate_items.iter().map(|item| &item.desc)) { + if set.len() >= DESC_INDEX_SHARD_LEN { + result.push((len, std::mem::replace(&mut set, String::new()))); + len = 0; + } else if len != 0 { + set.push('\n'); + } + set.push_str(&desc); + len += 1; + } + result.push((len, std::mem::replace(&mut set, String::new()))); + result + }; + + let descindex = { + let mut descindex = String::with_capacity(desc.len() * 4); + for &(len, _) in desc.iter() { + write_vlqhex_to_string(len.try_into().unwrap(), &mut descindex); + } + descindex + }; + + assert_eq!(crate_items.len() + 1, desc.iter().map(|(len, _)| *len).sum::()); + + // The index, which is actually used to search, is JSON + // It uses `JSON.parse(..)` to actually load, since JSON + // parses faster than the full JavaScript syntax. + let index = format!( r#"["{}",{}]"#, krate.name(tcx), serde_json::to_string(&CrateData { - doc: crate_doc, items: crate_items, paths: crate_paths, aliases: &aliases, associated_item_disambiguators: &associated_item_disambiguators, + descindex, }) .expect("failed serde conversion") // All these `replace` calls are because we have to go through JS string for JSON content. @@ -489,7 +530,45 @@ pub(crate) fn build_index<'tcx>( .replace('\'', r"\'") // We need to escape double quotes for the JSON. .replace("\\\"", "\\\\\"") - ) + ); + SerializedSearchIndex { index, desc } +} + +pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) { + let (sign, magnitude): (bool, u32) = + if n >= 0 { (false, n.try_into().unwrap()) } else { (true, (-n).try_into().unwrap()) }; + // zig-zag encoding + let value: u32 = (magnitude << 1) | (if sign { 1 } else { 0 }); + // Self-terminating hex use capital letters for everything but the + // least significant digit, which is lowercase. For example, decimal 17 + // would be `` Aa `` if zig-zag encoding weren't used. + // + // Zig-zag encoding, however, stores the sign bit as the last bit. + // This means, in the last hexit, 1 is actually `c`, -1 is `b` + // (`a` is the imaginary -0), and, because all the bits are shifted + // by one, `` A` `` is actually 8 and `` Aa `` is -8. + // + // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html + // describes the encoding in more detail. + let mut shift: u32 = 28; + let mut mask: u32 = 0xF0_00_00_00; + // first skip leading zeroes + while shift < 32 { + let hexit = (value & mask) >> shift; + if hexit != 0 || shift == 0 { + break; + } + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } + // now write the rest + while shift < 32 { + let hexit = (value & mask) >> shift; + let hex = char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); + string.push(hex); + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } } pub(crate) fn get_function_type_for_search<'tcx>( diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fbd45b2b48ef..c806bf1cc66f 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -24,6 +24,7 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::formats::Impl; use crate::html::format::Buffer; +use crate::html::render::search_index::SerializedSearchIndex; use crate::html::render::{AssocItemLink, ImplRenderingParameters}; use crate::html::{layout, static_files}; use crate::visit::DocVisitor; @@ -46,7 +47,7 @@ use crate::{try_err, try_none}; pub(super) fn write_shared( cx: &mut Context<'_>, krate: &Crate, - search_index: String, + search_index: SerializedSearchIndex, options: &RenderOptions, ) -> Result<(), Error> { // Write out the shared files. Note that these are shared among all rustdoc @@ -312,7 +313,7 @@ pub(super) fn write_shared( let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, krate.name(cx.tcx()).as_str()), &dst); - all_indexes.push(search_index); + all_indexes.push(search_index.index); krates.push(krate.name(cx.tcx()).to_string()); krates.sort(); @@ -335,6 +336,32 @@ else if (window.initSearch) window.initSearch(searchIndex); Ok(v.into_bytes()) })?; + let search_desc_dir = cx.dst.join(format!("search.desc/{krate}", krate = krate.name(cx.tcx()))); + if Path::new(&search_desc_dir).exists() { + try_err!(std::fs::remove_dir_all(&search_desc_dir), &search_desc_dir); + } + try_err!(std::fs::create_dir_all(&search_desc_dir), &search_desc_dir); + let kratename = krate.name(cx.tcx()).to_string(); + for (i, (_, data)) in search_index.desc.into_iter().enumerate() { + let output_filename = static_files::suffix_path( + &format!("{kratename}-desc-{i}-.js"), + &cx.shared.resource_suffix, + ); + let path = search_desc_dir.join(output_filename); + try_err!( + std::fs::write( + &path, + &format!( + r##"searchState.loadedDescShard({kratename}, {i}, {data})"##, + kratename = serde_json::to_string(&kratename).unwrap(), + data = serde_json::to_string(&data).unwrap(), + ) + .into_bytes() + ), + &path + ); + } + write_invocation_specific("crates.js", &|| { let krates = krates.iter().map(|k| format!("\"{k}\"")).join(","); Ok(format!("window.ALL_CRATES = [{krates}];").into_bytes()) diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index 1a34530c2d16..a1e9cc6dfa14 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 2015, + "ecmaVersion": 8, "sourceType": "module" }, "rules": { diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index b9a769a7c6da..2c1330614b8d 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -329,6 +329,26 @@ function preLoadCss(cssUrl) { search.innerHTML = "

" + searchState.loadingText + "

"; searchState.showResults(search); }, + descShards: new Map(), + loadDesc: async function({descShard, descIndex}) { + if (descShard.promise === null) { + descShard.promise = new Promise((resolve, reject) => { + descShard.resolve = resolve; + const ds = descShard; + const fname = `${ds.crate}-desc-${ds.shard}-`; + const url = resourcePath( + `search.desc/${descShard.crate}/${fname}`, + ".js", + ); + loadScript(url, reject); + }); + } + const list = await descShard.promise; + return list[descIndex]; + }, + loadedDescShard: function (crate, shard, data) { + this.descShards.get(crate)[shard].resolve(data.split("\n")); + }, }; const toggleAllDocsId = "toggle-all-docs"; @@ -381,7 +401,7 @@ function preLoadCss(cssUrl) { window.location.replace("#" + item.id); }, 0); } - } + }, ); } } @@ -585,7 +605,7 @@ function preLoadCss(cssUrl) { const script = document .querySelector("script[data-ignore-extern-crates]"); const ignoreExternCrates = new Set( - (script ? script.getAttribute("data-ignore-extern-crates") : "").split(",") + (script ? script.getAttribute("data-ignore-extern-crates") : "").split(","), ); for (const lib of libs) { if (lib === window.currentCrate || ignoreExternCrates.has(lib)) { @@ -1098,7 +1118,7 @@ function preLoadCss(cssUrl) { } else { wrapper.style.setProperty( "--popover-arrow-offset", - (wrapperPos.right - pos.right + 4) + "px" + (wrapperPos.right - pos.right + 4) + "px", ); } wrapper.style.visibility = ""; @@ -1680,7 +1700,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", - desiredSidebarSize + "px" + desiredSidebarSize + "px", ); }, 100); } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index cbfa503f2606..2732d6b15436 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -206,14 +206,14 @@ const editDistanceState = { // insertion this.current[j - 1] + 1, // substitution - this.prev[j - 1] + substitutionCost + this.prev[j - 1] + substitutionCost, ); if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) { // transposition this.current[j] = Math.min( this.current[j], - this.prevPrev[j - 2] + 1 + this.prevPrev[j - 2] + 1, ); } } @@ -856,8 +856,8 @@ function initSearch(rawSearchIndex) { parserState, parserState.userQuery.slice(start, end), generics, - isInGenerics - ) + isInGenerics, + ), ); } } @@ -1295,7 +1295,7 @@ function initSearch(rawSearchIndex) { * * @return {ResultsTable} */ - function execQuery(parsedQuery, filterCrates, currentCrate) { + async function execQuery(parsedQuery, filterCrates, currentCrate) { const results_others = new Map(), results_in_args = new Map(), results_returned = new Map(); @@ -1326,6 +1326,7 @@ function initSearch(rawSearchIndex) { duplicates.add(obj.fullPath); obj.href = res[1]; + obj.desc = result.desc; out.push(obj); if (out.length >= MAX_RESULTS) { break; @@ -1342,9 +1343,9 @@ function initSearch(rawSearchIndex) { * @param {Results} results * @param {boolean} isType * @param {string} preferredCrate - * @returns {[ResultObject]} + * @returns {Promise<[ResultObject]>} */ - function sortResults(results, isType, preferredCrate) { + async function sortResults(results, isType, preferredCrate) { const userQuery = parsedQuery.userQuery; const result_list = []; for (const result of results.values()) { @@ -1352,6 +1353,12 @@ function initSearch(rawSearchIndex) { result.word = searchIndex[result.id].word; result_list.push(result); } + for (const result of result_list) { + result.desc = searchState.loadDesc(result.item); + } + for (const result of result_list) { + result.desc = await result.desc; + } result_list.sort((aaa, bbb) => { let a, b; @@ -1422,8 +1429,8 @@ function initSearch(rawSearchIndex) { } // sort by description (no description goes later) - a = (aaa.item.desc === ""); - b = (bbb.item.desc === ""); + a = (aaa.desc === ""); + b = (bbb.desc === ""); if (a !== b) { return a - b; } @@ -1477,7 +1484,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensIn, solutionCb, - unboxingDepth + unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -1524,7 +1531,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, )) { continue; } @@ -1541,7 +1548,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensScratch, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, )) { return true; } @@ -1551,7 +1558,7 @@ function initSearch(rawSearchIndex) { whereClause, mgens ? new Map(mgens) : null, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, )) { return true; } @@ -1625,7 +1632,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensScratch, - unboxingDepth + unboxingDepth, ); if (!solution) { return false; @@ -1638,7 +1645,7 @@ function initSearch(rawSearchIndex) { whereClause, simplifiedMgens, solutionCb, - unboxingDepth + unboxingDepth, ); if (passesUnification) { return true; @@ -1646,7 +1653,7 @@ function initSearch(rawSearchIndex) { } return false; }, - unboxingDepth + unboxingDepth, ); if (passesUnification) { return true; @@ -1663,7 +1670,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, )) { continue; } @@ -1689,7 +1696,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensScratch, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, ); if (passesUnification) { return true; @@ -1820,7 +1827,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensIn, - unboxingDepth + unboxingDepth, ) { if (fnType.bindings.size < queryElem.bindings.size) { return false; @@ -1849,7 +1856,7 @@ function initSearch(rawSearchIndex) { // possible solutions return false; }, - unboxingDepth + unboxingDepth, ); return newSolutions; }); @@ -1887,7 +1894,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -1914,7 +1921,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensTmp, - unboxingDepth + unboxingDepth, ); } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { const simplifiedGenerics = [ @@ -1926,7 +1933,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + unboxingDepth, ); } return false; @@ -1975,7 +1982,7 @@ function initSearch(rawSearchIndex) { elem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, ); } if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && @@ -1989,7 +1996,7 @@ function initSearch(rawSearchIndex) { elem, whereClause, mgens, - unboxingDepth + unboxingDepth, ); } } @@ -2007,7 +2014,7 @@ function initSearch(rawSearchIndex) { return 0; } const maxPathEditDistance = Math.floor( - contains.reduce((acc, next) => acc + next.length, 0) / 3 + contains.reduce((acc, next) => acc + next.length, 0) / 3, ); let ret_dist = maxPathEditDistance + 1; const path = ty.path.split("::"); @@ -2066,7 +2073,8 @@ function initSearch(rawSearchIndex) { crate: item.crate, name: item.name, path: item.path, - desc: item.desc, + descShard: item.descShard, + descIndex: item.descIndex, ty: item.ty, parent: item.parent, type: item.type, @@ -2192,7 +2200,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxEditDistance + maxEditDistance, ) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; @@ -2204,7 +2212,7 @@ function initSearch(rawSearchIndex) { // atoms in the function not present in the query const tfpDist = compareTypeFingerprints( fullId, - parsedQuery.typeFingerprint + parsedQuery.typeFingerprint, ); if (tfpDist !== null) { const in_args = row.type && row.type.inputs @@ -2276,7 +2284,7 @@ function initSearch(rawSearchIndex) { const tfpDist = compareTypeFingerprints( row.id, - parsedQuery.typeFingerprint + parsedQuery.typeFingerprint, ); if (tfpDist === null) { return; @@ -2298,10 +2306,10 @@ function initSearch(rawSearchIndex) { row.type.where_clause, mgens, null, - 0 // unboxing depth + 0, // unboxing depth ); }, - 0 // unboxing depth + 0, // unboxing depth )) { return; } @@ -2419,7 +2427,7 @@ function initSearch(rawSearchIndex) { } return [typeNameIdMap.get(name).id, constraints]; - }) + }), ); } @@ -2446,7 +2454,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxEditDistance + maxEditDistance, ); } } @@ -2478,9 +2486,9 @@ function initSearch(rawSearchIndex) { } const ret = createQueryResults( - sortResults(results_in_args, true, currentCrate), - sortResults(results_returned, true, currentCrate), - sortResults(results_others, false, currentCrate), + await sortResults(results_in_args, true, currentCrate), + await sortResults(results_returned, true, currentCrate), + await sortResults(results_others, false, currentCrate), parsedQuery); handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); if (parsedQuery.error !== null && ret.others.length !== 0) { @@ -2581,14 +2589,14 @@ function initSearch(rawSearchIndex) { * @param {ParsedQuery} query * @param {boolean} display - True if this is the active tab */ - function addTab(array, query, display) { + async function addTab(array, query, display) { const extraClass = display ? " active" : ""; const output = document.createElement("div"); if (array.length > 0) { output.className = "search-results " + extraClass; - array.forEach(item => { + for (const item of array) { const name = item.name; const type = itemTypes[item.ty]; const longType = longItemTypes[item.ty]; @@ -2624,7 +2632,7 @@ ${item.displayPath}${name}\ link.appendChild(description); output.appendChild(link); - }); + } } else if (query.error === null) { output.className = "search-failed" + extraClass; output.innerHTML = "No results :(
" + @@ -2666,7 +2674,7 @@ ${item.displayPath}${name}\ * @param {boolean} go_to_first * @param {string} filterCrates */ - function showResults(results, go_to_first, filterCrates) { + async function showResults(results, go_to_first, filterCrates) { const search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true") @@ -2699,9 +2707,9 @@ ${item.displayPath}${name}\ currentResults = results.query.userQuery; - const ret_others = addTab(results.others, results.query, true); - const ret_in_args = addTab(results.in_args, results.query, false); - const ret_returned = addTab(results.returned, results.query, false); + const ret_others = await addTab(results.others, results.query, true); + const ret_in_args = await addTab(results.in_args, results.query, false); + const ret_returned = await addTab(results.returned, results.query, false); // Navigate to the relevant tab if the current tab is empty, like in case users search // for "-> String". If they had selected another tab previously, they have to click on @@ -2822,7 +2830,7 @@ ${item.displayPath}${name}\ * and display the results. * @param {boolean} [forced] */ - function search(forced) { + async function search(forced) { const query = parseQuery(searchState.input.value.trim()); let filterCrates = getFilterCrates(); @@ -2850,8 +2858,8 @@ ${item.displayPath}${name}\ // recent search query is added to the browser history. updateSearchHistory(buildUrl(query.original, filterCrates)); - showResults( - execQuery(query, filterCrates, window.currentCrate), + await showResults( + await execQuery(query, filterCrates, window.currentCrate), params.go_to_first, filterCrates); } @@ -2920,7 +2928,7 @@ ${item.displayPath}${name}\ pathIndex = type[PATH_INDEX_DATA]; generics = buildItemSearchTypeAll( type[GENERICS_DATA], - lowercasePaths + lowercasePaths, ); if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { bindings = new Map(type[BINDINGS_DATA].map(binding => { @@ -3030,101 +3038,49 @@ ${item.displayPath}${name}\ * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * - * @param {{ - * string: string, - * offset: number, - * backrefQueue: FunctionSearchType[] - * }} itemFunctionDecoder * @param {Array<{name: string, ty: number}>} lowercasePaths - * @param {Map} * * @return {null|FunctionSearchType} */ - function buildFunctionSearchType(itemFunctionDecoder, lowercasePaths) { - const c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - itemFunctionDecoder.offset += 1; - const [zero, ua, la, ob, cb] = ["0", "@", "`", "{", "}"].map(c => c.charCodeAt(0)); - // `` ` `` is used as a sentinel because it's fewer bytes than `null`, and decodes to zero - // `0` is a backref - if (c === la) { - return null; - } - // sixteen characters after "0" are backref - if (c >= zero && c < ua) { - return itemFunctionDecoder.backrefQueue[c - zero]; - } - if (c !== ob) { - throw ["Unexpected ", c, " in function: expected ", "{", "; this is a bug"]; - } - // call after consuming `{` - function decodeList() { - let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - const ret = []; - while (c !== cb) { - ret.push(decode()); - c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); + function buildFunctionSearchTypeCallback(lowercasePaths) { + return functionSearchType => { + if (functionSearchType === 0) { + return null; } - itemFunctionDecoder.offset += 1; // eat cb - return ret; - } - // consumes and returns a list or integer - function decode() { - let n = 0; - let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - if (c === ob) { - itemFunctionDecoder.offset += 1; - return decodeList(); - } - while (c < la) { - n = (n << 4) | (c & 0xF); - itemFunctionDecoder.offset += 1; - c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - } - // last character >= la - n = (n << 4) | (c & 0xF); - const [sign, value] = [n & 1, n >> 1]; - itemFunctionDecoder.offset += 1; - return sign ? -value : value; - } - const functionSearchType = decodeList(); - const INPUTS_DATA = 0; - const OUTPUT_DATA = 1; - let inputs, output; - if (typeof functionSearchType[INPUTS_DATA] === "number") { - inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; - } else { - inputs = buildItemSearchTypeAll( - functionSearchType[INPUTS_DATA], - lowercasePaths - ); - } - if (functionSearchType.length > 1) { - if (typeof functionSearchType[OUTPUT_DATA] === "number") { - output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; + const INPUTS_DATA = 0; + const OUTPUT_DATA = 1; + let inputs, output; + if (typeof functionSearchType[INPUTS_DATA] === "number") { + inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; } else { - output = buildItemSearchTypeAll( - functionSearchType[OUTPUT_DATA], - lowercasePaths + inputs = buildItemSearchTypeAll( + functionSearchType[INPUTS_DATA], + lowercasePaths, ); } - } else { - output = []; - } - const where_clause = []; - const l = functionSearchType.length; - for (let i = 2; i < l; ++i) { - where_clause.push(typeof functionSearchType[i] === "number" - ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] - : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); - } - const ret = { - inputs, output, where_clause, + if (functionSearchType.length > 1) { + if (typeof functionSearchType[OUTPUT_DATA] === "number") { + output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; + } else { + output = buildItemSearchTypeAll( + functionSearchType[OUTPUT_DATA], + lowercasePaths, + ); + } + } else { + output = []; + } + const where_clause = []; + const l = functionSearchType.length; + for (let i = 2; i < l; ++i) { + where_clause.push(typeof functionSearchType[i] === "number" + ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] + : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); + } + return { + inputs, output, where_clause, + }; }; - itemFunctionDecoder.backrefQueue.unshift(ret); - if (itemFunctionDecoder.backrefQueue.length > 16) { - itemFunctionDecoder.backrefQueue.pop(); - } - return ret; } /** @@ -3245,6 +3201,68 @@ ${item.displayPath}${name}\ return functionTypeFingerprint[(fullId * 4) + 3]; } + class VlqHexDecoder { + constructor(string, cons) { + this.string = string; + this.cons = cons; + this.offset = 0; + this.backrefQueue = []; + } + // call after consuming `{` + decodeList() { + const cb = "}".charCodeAt(0); + let c = this.string.charCodeAt(this.offset); + const ret = []; + while (c !== cb) { + ret.push(this.decode()); + c = this.string.charCodeAt(this.offset); + } + this.offset += 1; // eat cb + return ret; + } + // consumes and returns a list or integer + decode() { + const [ob, la] = ["{", "`"].map(c => c.charCodeAt(0)); + let n = 0; + let c = this.string.charCodeAt(this.offset); + if (c === ob) { + this.offset += 1; + return this.decodeList(); + } + while (c < la) { + n = (n << 4) | (c & 0xF); + this.offset += 1; + c = this.string.charCodeAt(this.offset); + } + // last character >= la + n = (n << 4) | (c & 0xF); + const [sign, value] = [n & 1, n >> 1]; + this.offset += 1; + return sign ? -value : value; + } + next() { + const c = this.string.charCodeAt(this.offset); + const [zero, ua, la] = ["0", "@", "`"].map(c => c.charCodeAt(0)); + // sixteen characters after "0" are backref + if (c >= zero && c < ua) { + this.offset += 1; + return this.backrefQueue[c - zero]; + } + // special exception: 0 doesn't use backref encoding + // it's already one character, and it's always nullish + if (c === la) { + this.offset += 1; + return this.cons(0); + } + const result = this.cons(this.decode()); + this.backrefQueue.unshift(result); + if (this.backrefQueue.length > 16) { + this.backrefQueue.pop(); + } + return result; + } + } + /** * Convert raw search index into in-memory search index. * @@ -3271,18 +3289,32 @@ ${item.displayPath}${name}\ id = 0; for (const [crate, crateCorpus] of rawSearchIndex) { + // a string representing the lengths of each description shard + // a string representing the list of function types + const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); + let descShard = { + crate, + shard: 0, + start: 0, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + const descShardList = [ descShard ]; + // This object should have exactly the same set of fields as the "row" // object defined below. Your JavaScript runtime will thank you. // https://mathiasbynens.be/notes/shapes-ics const crateRow = { - crate: crate, + crate, ty: 3, // == ExternCrate name: crate, path: "", - desc: crateCorpus.doc, + descShard, + descIndex: 0, parent: undefined, type: null, - id: id, + id, word: crate, normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), deprecated: null, @@ -3302,16 +3334,8 @@ ${item.displayPath}${name}\ // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present, // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11 const itemPaths = new Map(crateCorpus.q); - // an array of (String) descriptions - const itemDescs = crateCorpus.d; // an array of (Number) the parent path index + 1 to `paths`, or 0 if none const itemParentIdxs = crateCorpus.i; - // a string representing the list of function types - const itemFunctionDecoder = { - string: crateCorpus.f, - offset: 0, - backrefQueue: [], - }; // an array of (Number) indices for the deprecated items const deprecatedItems = new Set(crateCorpus.c); // an array of (Number) indices for the deprecated items @@ -3326,6 +3350,12 @@ ${item.displayPath}${name}\ // an array of [{name: String, ty: Number}] const lowercasePaths = []; + // a string representing the list of function types + const itemFunctionDecoder = new VlqHexDecoder( + crateCorpus.f, + buildFunctionSearchTypeCallback(lowercasePaths), + ); + // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = paths.length; @@ -3353,13 +3383,26 @@ ${item.displayPath}${name}\ // faster analysis operations lastPath = ""; len = itemTypes.length; + let descIndex = 1; for (let i = 0; i < len; ++i) { + if (descIndex >= descShard.len) { + descShard = { + crate, + shard: descShard.shard + 1, + start: descShard.start + descShard.len, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + descIndex = 0; + descShardList.push(descShard); + } let word = ""; if (typeof itemNames[i] === "string") { word = itemNames[i].toLowerCase(); } const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; - const type = buildFunctionSearchType(itemFunctionDecoder, lowercasePaths); + const type = itemFunctionDecoder.next(); if (type !== null) { if (type) { const fp = functionTypeFingerprint.subarray(id * 4, (id + 1) * 4); @@ -3380,14 +3423,15 @@ ${item.displayPath}${name}\ // This object should have exactly the same set of fields as the "crateRow" // object defined above. const row = { - crate: crate, + crate, ty: itemTypes.charCodeAt(i) - charA, name: itemNames[i], - path: path, - desc: itemDescs[i], + path, + descShard, + descIndex, parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, type, - id: id, + id, word, normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), deprecated: deprecatedItems.has(i), @@ -3396,6 +3440,7 @@ ${item.displayPath}${name}\ id += 1; searchIndex.push(row); lastPath = row.path; + descIndex += 1; } if (aliases) { @@ -3419,6 +3464,7 @@ ${item.displayPath}${name}\ } } currentIndex += itemTypes.length; + searchState.descShards.set(crate, descShardList); } // Drop the (rather large) hash table used for reusing function items TYPES_POOL = new Map(); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index bda7b3c647e7..73c543567c07 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -211,14 +211,14 @@ function updateSidebarWidth() { if (desktopSidebarWidth && desktopSidebarWidth !== "null") { document.documentElement.style.setProperty( "--desktop-sidebar-width", - desktopSidebarWidth + "px" + desktopSidebarWidth + "px", ); } const srcSidebarWidth = getSettingValue("src-sidebar-width"); if (srcSidebarWidth && srcSidebarWidth !== "null") { document.documentElement.style.setProperty( "--src-sidebar-width", - srcSidebarWidth + "px" + srcSidebarWidth + "px", ); } } diff --git a/src/tools/rustdoc-js/.eslintrc.js b/src/tools/rustdoc-js/.eslintrc.js index 4ab3a3157330..b9d0e251c242 100644 --- a/src/tools/rustdoc-js/.eslintrc.js +++ b/src/tools/rustdoc-js/.eslintrc.js @@ -6,7 +6,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 2015, + "ecmaVersion": 8, "sourceType": "module" }, "rules": { diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 86881ef362e9..ae4121cc03f5 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -1,3 +1,4 @@ +/* global globalThis */ const fs = require("fs"); const path = require("path"); @@ -133,7 +134,7 @@ function valueCheck(fullPath, expected, result, error_text, queryName) { expected_value, result.get(key), error_text, - queryName + queryName, ); } else { error_text.push(`${queryName}==> EXPECTED has extra key in map from field ` + @@ -212,11 +213,11 @@ function runParser(query, expected, parseQuery, queryName) { return error_text; } -function runSearch(query, expected, doSearch, loadedFile, queryName) { +async function runSearch(query, expected, doSearch, loadedFile, queryName) { const ignore_order = loadedFile.ignore_order; const exact_check = loadedFile.exact_check; - const results = doSearch(query, loadedFile.FILTER_CRATE); + const results = await doSearch(query, loadedFile.FILTER_CRATE); const error_text = []; for (const key in expected) { @@ -238,7 +239,7 @@ function runSearch(query, expected, doSearch, loadedFile, queryName) { } let prev_pos = -1; - entry.forEach((elem, index) => { + for (const [index, elem] of entry.entries()) { const entry_pos = lookForEntry(elem, results[key]); if (entry_pos === -1) { error_text.push(queryName + "==> Result not found in '" + key + "': '" + @@ -260,13 +261,13 @@ function runSearch(query, expected, doSearch, loadedFile, queryName) { } else { prev_pos = entry_pos; } - }); + } } return error_text; } -function runCorrections(query, corrections, getCorrections, loadedFile) { - const qc = getCorrections(query, loadedFile.FILTER_CRATE); +async function runCorrections(query, corrections, getCorrections, loadedFile) { + const qc = await getCorrections(query, loadedFile.FILTER_CRATE); const error_text = []; if (corrections === null) { @@ -299,18 +300,27 @@ function checkResult(error_text, loadedFile, displaySuccess) { return 1; } -function runCheckInner(callback, loadedFile, entry, getCorrections, extra) { +async function runCheckInner(callback, loadedFile, entry, getCorrections, extra) { if (typeof entry.query !== "string") { console.log("FAILED"); console.log("==> Missing `query` field"); return false; } - let error_text = callback(entry.query, entry, extra ? "[ query `" + entry.query + "`]" : ""); + let error_text = await callback( + entry.query, + entry, + extra ? "[ query `" + entry.query + "`]" : "", + ); if (checkResult(error_text, loadedFile, false) !== 0) { return false; } if (entry.correction !== undefined) { - error_text = runCorrections(entry.query, entry.correction, getCorrections, loadedFile); + error_text = await runCorrections( + entry.query, + entry.correction, + getCorrections, + loadedFile, + ); if (checkResult(error_text, loadedFile, false) !== 0) { return false; } @@ -318,16 +328,16 @@ function runCheckInner(callback, loadedFile, entry, getCorrections, extra) { return true; } -function runCheck(loadedFile, key, getCorrections, callback) { +async function runCheck(loadedFile, key, getCorrections, callback) { const expected = loadedFile[key]; if (Array.isArray(expected)) { for (const entry of expected) { - if (!runCheckInner(callback, loadedFile, entry, getCorrections, true)) { + if (!await runCheckInner(callback, loadedFile, entry, getCorrections, true)) { return 1; } } - } else if (!runCheckInner(callback, loadedFile, expected, getCorrections, false)) { + } else if (!await runCheckInner(callback, loadedFile, expected, getCorrections, false)) { return 1; } console.log("OK"); @@ -338,7 +348,7 @@ function hasCheck(content, checkName) { return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`); } -function runChecks(testFile, doSearch, parseQuery, getCorrections) { +async function runChecks(testFile, doSearch, parseQuery, getCorrections) { let checkExpected = false; let checkParsed = false; let testFileContent = readFile(testFile); @@ -367,12 +377,12 @@ function runChecks(testFile, doSearch, parseQuery, getCorrections) { let res = 0; if (checkExpected) { - res += runCheck(loadedFile, "EXPECTED", getCorrections, (query, expected, text) => { + res += await runCheck(loadedFile, "EXPECTED", getCorrections, (query, expected, text) => { return runSearch(query, expected, doSearch, loadedFile, text); }); } if (checkParsed) { - res += runCheck(loadedFile, "PARSED", getCorrections, (query, expected, text) => { + res += await runCheck(loadedFile, "PARSED", getCorrections, (query, expected, text) => { return runParser(query, expected, parseQuery, text); }); } @@ -393,6 +403,35 @@ function loadSearchJS(doc_folder, resource_suffix) { const searchIndexJs = path.join(doc_folder, "search-index" + resource_suffix + ".js"); const searchIndex = require(searchIndexJs); + globalThis.searchState = { + descShards: new Map(), + loadDesc: async function({descShard, descIndex}) { + if (descShard.promise === null) { + descShard.promise = new Promise((resolve, reject) => { + descShard.resolve = resolve; + const ds = descShard; + const fname = `${ds.crate}-desc-${ds.shard}-${resource_suffix}.js`; + fs.readFile( + `${doc_folder}/search.desc/${descShard.crate}/${fname}`, + (err, data) => { + if (err) { + reject(err); + } else { + eval(data.toString("utf8")); + } + }, + ); + }); + } + const list = await descShard.promise; + return list[descIndex]; + }, + loadedDescShard: function (crate, shard, data) { + //console.log(this.descShards); + this.descShards.get(crate)[shard].resolve(data.split("\n")); + }, + }; + const staticFiles = path.join(doc_folder, "static.files"); const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/)); const searchModule = require(path.join(staticFiles, searchJs)); @@ -474,7 +513,7 @@ function parseOptions(args) { return null; } -function main(argv) { +async function main(argv) { const opts = parseOptions(argv.slice(2)); if (opts === null) { return 1; @@ -482,7 +521,7 @@ function main(argv) { const parseAndSearch = loadSearchJS( opts["doc_folder"], - opts["resource_suffix"] + opts["resource_suffix"], ); let errors = 0; @@ -494,21 +533,34 @@ function main(argv) { }; if (opts["test_file"].length !== 0) { - opts["test_file"].forEach(file => { + for (const file of opts["test_file"]) { process.stdout.write(`Testing ${file} ... `); - errors += runChecks(file, doSearch, parseAndSearch.parseQuery, getCorrections); - }); + errors += await runChecks(file, doSearch, parseAndSearch.parseQuery, getCorrections); + } } else if (opts["test_folder"].length !== 0) { - fs.readdirSync(opts["test_folder"]).forEach(file => { + for (const file of fs.readdirSync(opts["test_folder"])) { if (!file.endsWith(".js")) { - return; + continue; } process.stdout.write(`Testing ${file} ... `); - errors += runChecks(path.join(opts["test_folder"], file), doSearch, + errors += await runChecks(path.join(opts["test_folder"], file), doSearch, parseAndSearch.parseQuery, getCorrections); - }); + } } return errors > 0 ? 1 : 0; } -process.exit(main(process.argv)); +main(process.argv).catch(e => { + console.log(e); + process.exit(1); +}).then(x => process.exit(x)); + +process.on("beforeExit", () => { + console.log("process did not complete"); + process.exit(1); +}); + +/*process.on("uncaughtException", (err) => { + console.log(`Uncaught Exception: ${err.message}`); + process.exit(1); +});*/ diff --git a/tests/rustdoc/search-index-summaries.rs b/tests/rustdoc/search-index-summaries.rs index efd366405bfe..529b42d0ca90 100644 --- a/tests/rustdoc/search-index-summaries.rs +++ b/tests/rustdoc/search-index-summaries.rs @@ -1,6 +1,6 @@ #![crate_name = "foo"] -// @hasraw 'search-index.js' 'Foo short link.' +// @hasraw 'search.desc/foo/foo-desc-0-.js' 'Foo short link.' // @!hasraw - 'www.example.com' // @!hasraw - 'More Foo.' From 5a95a53a390d214cfc1e3e6797ddb7214b4a440a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 17 Mar 2024 11:04:52 +0200 Subject: [PATCH 010/192] Merge commit '5ecace48f693afaa6adf8cb23086b651db3aec96' into sync-from-ra --- .github/workflows/release.yaml | 9 - Cargo.lock | 16 +- Cargo.toml | 4 + crates/base-db/Cargo.toml | 2 + crates/base-db/src/change.rs | 10 +- crates/base-db/src/lib.rs | 43 + crates/cfg/Cargo.toml | 2 +- crates/cfg/src/cfg_expr.rs | 2 +- crates/flycheck/src/test_runner.rs | 9 +- crates/hir-def/src/body.rs | 25 +- crates/hir-def/src/body/lower.rs | 102 ++- crates/hir-def/src/body/pretty.rs | 11 +- crates/hir-def/src/body/scope.rs | 3 + crates/hir-def/src/child_by_source.rs | 2 +- crates/hir-def/src/data.rs | 6 +- crates/hir-def/src/data/adt.rs | 8 +- crates/hir-def/src/db.rs | 10 - crates/hir-def/src/item_scope.rs | 26 +- crates/hir-def/src/item_tree.rs | 5 +- crates/hir-def/src/item_tree/lower.rs | 19 +- crates/hir-def/src/item_tree/pretty.rs | 6 +- crates/hir-def/src/item_tree/tests.rs | 2 +- crates/hir-def/src/lib.rs | 38 +- .../builtin_derive_macro.rs | 118 +++ .../hir-def/src/macro_expansion_tests/mbe.rs | 2 +- .../src/macro_expansion_tests/mbe/matching.rs | 2 +- .../mbe/tt_conversion.rs | 2 +- crates/hir-def/src/nameres.rs | 31 +- crates/hir-def/src/nameres/attr_resolution.rs | 12 +- crates/hir-def/src/nameres/collector.rs | 26 +- .../hir-def/src/nameres/tests/incremental.rs | 7 +- crates/hir-def/src/src.rs | 16 +- crates/hir-expand/src/attrs.rs | 18 +- crates/hir-expand/src/builtin_attr_macro.rs | 18 +- crates/hir-expand/src/builtin_derive_macro.rs | 2 +- crates/hir-expand/src/builtin_fn_macro.rs | 117 ++- crates/hir-expand/src/cfg_process.rs | 327 ++++++++ crates/hir-expand/src/change.rs | 2 +- crates/hir-expand/src/db.rs | 546 ++++++------- crates/hir-expand/src/declarative.rs | 5 +- crates/hir-expand/src/eager.rs | 61 +- crates/hir-expand/src/files.rs | 113 ++- crates/hir-expand/src/fixup.rs | 16 +- crates/hir-expand/src/hygiene.rs | 7 +- crates/hir-expand/src/lib.rs | 139 +++- crates/hir-expand/src/quote.rs | 7 +- crates/hir-expand/src/span_map.rs | 65 +- crates/hir-ty/Cargo.toml | 8 +- crates/hir-ty/src/autoderef.rs | 6 +- crates/hir-ty/src/infer.rs | 12 +- crates/hir-ty/src/infer/coerce.rs | 2 +- crates/hir-ty/src/infer/expr.rs | 24 +- crates/hir-ty/src/infer/path.rs | 2 +- crates/hir-ty/src/infer/unify.rs | 74 +- crates/hir-ty/src/layout.rs | 4 +- crates/hir-ty/src/method_resolution.rs | 156 ++-- crates/hir-ty/src/mir.rs | 1 + crates/hir-ty/src/mir/eval.rs | 4 + crates/hir-ty/src/mir/lower.rs | 42 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 33 +- crates/hir-ty/src/tests.rs | 25 +- crates/hir-ty/src/tests/incremental.rs | 7 +- crates/hir-ty/src/tests/method_resolution.rs | 37 +- crates/hir-ty/src/tests/simple.rs | 3 + crates/hir/src/diagnostics.rs | 4 +- crates/hir/src/display.rs | 90 ++- crates/hir/src/has_source.rs | 14 + crates/hir/src/lib.rs | 114 ++- crates/hir/src/semantics.rs | 58 +- crates/hir/src/semantics/source_to_def.rs | 12 +- crates/hir/src/source_analyzer.rs | 7 +- crates/hir/src/symbols.rs | 5 +- .../src/handlers/extract_module.rs | 746 ++++++++---------- .../src/handlers/generate_function.rs | 2 +- .../ide-assists/src/handlers/inline_call.rs | 2 +- .../src/completions/env_vars.rs | 43 +- .../src/completions/item_list/trait_impl.rs | 2 +- crates/ide-completion/src/completions/mod_.rs | 8 +- crates/ide-completion/src/item.rs | 1 + crates/ide-completion/src/lib.rs | 2 +- crates/ide-db/src/apply_change.rs | 1 + crates/ide-db/src/defs.rs | 11 +- crates/ide-db/src/helpers.rs | 2 +- crates/ide-db/src/lib.rs | 12 +- crates/ide-db/src/search.rs | 13 +- .../src/handlers/incorrect_case.rs | 2 +- .../src/handlers/mutability_errors.rs | 10 +- .../src/handlers/unresolved_field.rs | 313 +++++++- .../src/handlers/unused_variables.rs | 121 ++- crates/ide-ssr/src/matching.rs | 20 +- crates/ide-ssr/src/search.rs | 11 +- crates/ide/src/goto_definition.rs | 51 +- crates/ide/src/hover/render.rs | 6 +- crates/ide/src/hover/tests.rs | 354 ++++++--- crates/ide/src/inlay_hints/implicit_drop.rs | 4 + crates/ide/src/lib.rs | 2 +- crates/ide/src/navigation_target.rs | 43 +- crates/ide/src/references.rs | 2 +- crates/ide/src/runnables.rs | 22 +- crates/ide/src/syntax_highlighting.rs | 8 +- crates/ide/src/syntax_highlighting/html.rs | 1 + crates/ide/src/syntax_highlighting/tags.rs | 4 + .../test_data/highlight_assoc_functions.html | 1 + .../test_data/highlight_attributes.html | 1 + .../test_data/highlight_block_mod_items.html | 1 + .../test_data/highlight_crate_root.html | 1 + .../test_data/highlight_default_library.html | 1 + .../test_data/highlight_doctest.html | 1 + .../test_data/highlight_extern_crate.html | 1 + .../test_data/highlight_general.html | 1 + .../test_data/highlight_injection.html | 1 + .../test_data/highlight_keywords.html | 1 + .../test_data/highlight_lifetimes.html | 1 + .../test_data/highlight_macros.html | 15 +- .../highlight_module_docs_inline.html | 1 + .../highlight_module_docs_outline.html | 1 + .../test_data/highlight_operators.html | 1 + .../test_data/highlight_rainbow.html | 1 + .../test_data/highlight_strings.html | 1 + .../test_data/highlight_unsafe.html | 1 + crates/ide/src/test_explorer.rs | 25 +- crates/load-cargo/src/lib.rs | 10 +- crates/mbe/src/benchmark.rs | 25 +- crates/mbe/src/syntax_bridge.rs | 62 +- crates/mbe/src/syntax_bridge/tests.rs | 5 +- crates/proc-macro-srv/src/tests/mod.rs | 177 +++-- crates/proc-macro-srv/src/tests/utils.rs | 2 +- crates/rust-analyzer/Cargo.toml | 1 + crates/rust-analyzer/src/bin/main.rs | 18 +- .../rust-analyzer/src/cli/analysis_stats.rs | 8 +- crates/rust-analyzer/src/cli/rustc_tests.rs | 2 +- crates/rust-analyzer/src/cli/scip.rs | 4 +- crates/rust-analyzer/src/config.rs | 27 +- .../rust-analyzer/src/diagnostics/to_proto.rs | 2 +- crates/rust-analyzer/src/global_state.rs | 2 +- .../src/integrated_benchmarks.rs | 20 +- crates/rust-analyzer/src/lsp/ext.rs | 7 + .../rust-analyzer/src/lsp/semantic_tokens.rs | 2 + crates/rust-analyzer/src/lsp/to_proto.rs | 47 +- crates/rust-analyzer/src/main_loop.rs | 33 +- .../rust-analyzer/tests/slow-tests/support.rs | 2 +- crates/span/src/hygiene.rs | 12 +- crates/span/src/lib.rs | 33 +- crates/span/src/map.rs | 59 +- crates/stdx/src/anymap.rs | 15 - crates/syntax/src/ast/node_ext.rs | 11 + crates/test-fixture/src/lib.rs | 8 +- crates/tt/src/lib.rs | 48 +- docs/dev/lsp-extensions.md | 9 +- editors/code/language-configuration.json | 2 +- editors/code/package-lock.json | 6 +- editors/code/src/lsp_ext.ts | 3 + editors/code/src/test_explorer.ts | 6 + 153 files changed, 3382 insertions(+), 1925 deletions(-) create mode 100644 crates/hir-expand/src/cfg_process.rs diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ac536d0fddea..dc0a6c2d91f4 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -36,7 +36,6 @@ jobs: - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu code-target: linux-x64 - container: ubuntu:18.04 - os: ubuntu-20.04 target: aarch64-unknown-linux-gnu code-target: linux-arm64 @@ -63,14 +62,6 @@ jobs: with: fetch-depth: ${{ env.FETCH_DEPTH }} - - name: Install toolchain dependencies - if: matrix.container == 'ubuntu:18.04' - shell: bash - run: | - apt-get update && apt-get install -y build-essential curl - curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y - echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH - - name: Install Rust toolchain run: | rustup update --no-self-update stable diff --git a/Cargo.lock b/Cargo.lock index 903141eee9af..68ed32391b7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,7 @@ version = "0.0.0" dependencies = [ "cfg", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lz4_flex", "rustc-hash", "salsa", "semver", @@ -134,9 +135,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg" @@ -874,9 +875,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", "windows-targets 0.52.4", @@ -992,6 +993,12 @@ dependencies = [ "url", ] +[[package]] +name = "lz4_flex" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" + [[package]] name = "mbe" version = "0.0.0" @@ -1597,6 +1604,7 @@ dependencies = [ "rayon", "rustc-hash", "scip", + "semver", "serde", "serde_json", "sourcegen", diff --git a/Cargo.toml b/Cargo.toml index 440f46a938b9..0679522efd66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,10 @@ anyhow = "1.0.75" arrayvec = "0.7.4" bitflags = "2.4.1" cargo_metadata = "0.18.1" +chalk-solve = { version = "0.96.0", default-features = false } +chalk-ir = "0.96.0" +chalk-recursive = { version = "0.96.0", default-features = false } +chalk-derive = "0.96.0" command-group = "2.0.1" crossbeam-channel = "0.5.8" dissimilar = "1.0.7" diff --git a/crates/base-db/Cargo.toml b/crates/base-db/Cargo.toml index 118abf5d6eb8..4ab99fc33c46 100644 --- a/crates/base-db/Cargo.toml +++ b/crates/base-db/Cargo.toml @@ -12,6 +12,8 @@ rust-version.workspace = true doctest = false [dependencies] +lz4_flex = { version = "0.11", default-features = false } + la-arena.workspace = true salsa.workspace = true rustc-hash.workspace = true diff --git a/crates/base-db/src/change.rs b/crates/base-db/src/change.rs index 003ffb24d9d0..f202a885e278 100644 --- a/crates/base-db/src/change.rs +++ b/crates/base-db/src/change.rs @@ -7,13 +7,13 @@ use salsa::Durability; use triomphe::Arc; use vfs::FileId; -use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId}; +use crate::{CrateGraph, SourceDatabaseExt, SourceDatabaseExt2, SourceRoot, SourceRootId}; /// Encapsulate a bunch of raw `.set` calls on the database. #[derive(Default)] pub struct FileChange { pub roots: Option>, - pub files_changed: Vec<(FileId, Option>)>, + pub files_changed: Vec<(FileId, Option)>, pub crate_graph: Option, } @@ -42,7 +42,7 @@ impl FileChange { self.roots = Some(roots); } - pub fn change_file(&mut self, file_id: FileId, new_text: Option>) { + pub fn change_file(&mut self, file_id: FileId, new_text: Option) { self.files_changed.push((file_id, new_text)) } @@ -68,8 +68,8 @@ impl FileChange { let source_root = db.source_root(source_root_id); let durability = durability(&source_root); // XXX: can't actually remove the file, just reset the text - let text = text.unwrap_or_else(|| Arc::from("")); - db.set_file_text_with_durability(file_id, text, durability) + let text = text.unwrap_or_default(); + db.set_file_text_with_durability(file_id, &text, durability) } if let Some(crate_graph) = self.crate_graph { db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH); diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 758d2a45c8fc..5dcb580723fa 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -7,6 +7,7 @@ mod input; use std::panic; +use salsa::Durability; use syntax::{ast, Parse, SourceFile}; use triomphe::Arc; @@ -42,6 +43,7 @@ pub trait Upcast { fn upcast(&self) -> &T; } +pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16; pub const DEFAULT_PARSE_LRU_CAP: usize = 128; pub const DEFAULT_BORROWCK_LRU_CAP: usize = 1024; @@ -89,7 +91,10 @@ fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse { #[salsa::query_group(SourceDatabaseExtStorage)] pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] + fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>; + fn file_text(&self, file_id: FileId) -> Arc; + /// Path to a file, relative to the root of its source root. /// Source root of the file. #[salsa::input] @@ -101,6 +106,44 @@ pub trait SourceDatabaseExt: SourceDatabase { fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>; } +fn file_text(db: &dyn SourceDatabaseExt, file_id: FileId) -> Arc { + let bytes = db.compressed_file_text(file_id); + let bytes = + lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail"); + let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8"); + Arc::from(text) +} + +pub trait SourceDatabaseExt2 { + fn set_file_text(&mut self, file_id: FileId, text: &str) { + self.set_file_text_with_durability(file_id, text, Durability::LOW); + } + + fn set_file_text_with_durability( + &mut self, + file_id: FileId, + text: &str, + durability: Durability, + ); +} + +impl SourceDatabaseExt2 for Db { + fn set_file_text_with_durability( + &mut self, + file_id: FileId, + text: &str, + durability: Durability, + ) { + let bytes = text.as_bytes(); + let compressed = lz4_flex::compress_prepend_size(bytes); + self.set_compressed_file_text_with_durability( + file_id, + Arc::from(compressed.as_slice()), + durability, + ) + } +} + fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[CrateId]> { let graph = db.crate_graph(); let mut crates = graph diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml index fbda065b10f3..9b3a5026ac80 100644 --- a/crates/cfg/Cargo.toml +++ b/crates/cfg/Cargo.toml @@ -31,4 +31,4 @@ mbe.workspace = true syntax.workspace = true [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index 4be6ae7481d8..b7dbb7b5fdd7 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs @@ -47,6 +47,7 @@ impl CfgExpr { pub fn parse(tt: &tt::Subtree) -> CfgExpr { next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) } + /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option { match self { @@ -62,7 +63,6 @@ impl CfgExpr { } } } - fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { let name = match it.next() { None => return None, diff --git a/crates/flycheck/src/test_runner.rs b/crates/flycheck/src/test_runner.rs index 6dac5899ee3a..31378716b3ec 100644 --- a/crates/flycheck/src/test_runner.rs +++ b/crates/flycheck/src/test_runner.rs @@ -28,19 +28,20 @@ pub enum CargoTestMessage { }, Suite, Finished, + Custom { + text: String, + }, } impl ParseFromLine for CargoTestMessage { - fn from_line(line: &str, error: &mut String) -> Option { + fn from_line(line: &str, _: &mut String) -> Option { let mut deserializer = serde_json::Deserializer::from_str(line); deserializer.disable_recursion_limit(); if let Ok(message) = CargoTestMessage::deserialize(&mut deserializer) { return Some(message); } - error.push_str(line); - error.push('\n'); - None + Some(CargoTestMessage::Custom { text: line.to_owned() }) } fn from_eof() -> Option { diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 37d37fd33115..c9f1add27510 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -10,7 +10,6 @@ use std::ops::Index; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use either::Either; use hir_expand::{name::Name, HirFileId, InFile}; use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashMap; @@ -45,7 +44,8 @@ pub struct Body { /// /// If this `Body` is for the body of a constant, this will just be /// empty. - pub params: Vec, + pub params: Box<[PatId]>, + pub self_param: Option, /// The `ExprId` of the actual body expression. pub body_expr: ExprId, /// Block expressions in this body that may contain inner items. @@ -55,7 +55,7 @@ pub struct Body { pub type ExprPtr = AstPtr; pub type ExprSource = InFile; -pub type PatPtr = AstPtr>; +pub type PatPtr = AstPtr; pub type PatSource = InFile; pub type LabelPtr = AstPtr; @@ -63,6 +63,7 @@ pub type LabelSource = InFile; pub type FieldPtr = AstPtr; pub type FieldSource = InFile; + pub type PatFieldPtr = AstPtr; pub type PatFieldSource = InFile; @@ -88,6 +89,8 @@ pub struct BodySourceMap { label_map: FxHashMap, label_map_back: ArenaMap, + self_param: Option>>, + /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). /// Instead, we use id of expression (`92`) to identify the field. field_map_back: FxHashMap, @@ -215,10 +218,11 @@ impl Body { fn shrink_to_fit(&mut self) { let Self { body_expr: _, + params: _, + self_param: _, block_scopes, exprs, labels, - params, pats, bindings, binding_owners, @@ -226,7 +230,6 @@ impl Body { block_scopes.shrink_to_fit(); exprs.shrink_to_fit(); labels.shrink_to_fit(); - params.shrink_to_fit(); pats.shrink_to_fit(); bindings.shrink_to_fit(); binding_owners.shrink_to_fit(); @@ -297,6 +300,7 @@ impl Default for Body { params: Default::default(), block_scopes: Default::default(), binding_owners: Default::default(), + self_param: Default::default(), } } } @@ -354,14 +358,12 @@ impl BodySourceMap { self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax) } - pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option { - let src = node.map(|it| AstPtr::new(it).wrap_left()); - self.pat_map.get(&src).cloned() + pub fn self_param_syntax(&self) -> Option>> { + self.self_param } - pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option { - let src = node.map(|it| AstPtr::new(it).wrap_right()); - self.pat_map.get(&src).cloned() + pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option { + self.pat_map.get(&node.map(AstPtr::new)).cloned() } pub fn label_syntax(&self, label: LabelId) -> LabelSource { @@ -401,6 +403,7 @@ impl BodySourceMap { fn shrink_to_fit(&mut self) { let Self { + self_param: _, expr_map, expr_map_back, pat_map, diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 666912778949..340e95dbc2f4 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -4,7 +4,6 @@ use std::mem; use base_db::CrateId; -use either::Either; use hir_expand::{ name::{name, AsName, Name}, ExpandError, InFile, @@ -29,7 +28,6 @@ use crate::{ db::DefDatabase, expander::Expander, hir::{ - dummy_expr_id, format_args::{ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions, @@ -66,16 +64,7 @@ pub(super) fn lower( def_map: expander.module.def_map(db), source_map: BodySourceMap::default(), ast_id_map: db.ast_id_map(expander.current_file_id()), - body: Body { - exprs: Default::default(), - pats: Default::default(), - bindings: Default::default(), - binding_owners: Default::default(), - labels: Default::default(), - params: Vec::new(), - body_expr: dummy_expr_id(), - block_scopes: Vec::new(), - }, + body: Body::default(), expander, current_try_block_label: None, is_lowering_assignee_expr: false, @@ -191,35 +180,35 @@ impl ExprCollector<'_> { is_async_fn: bool, ) -> (Body, BodySourceMap) { if let Some((param_list, mut attr_enabled)) = param_list { + let mut params = vec![]; if let Some(self_param) = param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) { let is_mutable = self_param.mut_token().is_some() && self_param.amp_token().is_none(); - let ptr = AstPtr::new(&Either::Right(self_param)); let binding_id: la_arena::Idx = self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false)); - let param_pat = self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, ptr); - self.add_definition_to_binding(binding_id, param_pat); - self.body.params.push(param_pat); + self.body.self_param = Some(binding_id); + self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param))); } for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) { let param_pat = self.collect_pat_top(param.pat()); - self.body.params.push(param_pat); + params.push(param_pat); } + self.body.params = params.into_boxed_slice(); }; self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| { if is_async_fn { match body { Some(e) => { + let syntax_ptr = AstPtr::new(&e); let expr = this.collect_expr(e); - this.alloc_expr_desugared(Expr::Async { - id: None, - statements: Box::new([]), - tail: Some(expr), - }) + this.alloc_expr_desugared_with_ptr( + Expr::Async { id: None, statements: Box::new([]), tail: Some(expr) }, + syntax_ptr, + ) } None => this.missing_expr(), } @@ -405,7 +394,7 @@ impl ExprCollector<'_> { } ast::Expr::ParenExpr(e) => { let inner = self.collect_expr_opt(e.expr()); - // make the paren expr point to the inner expression as well + // make the paren expr point to the inner expression as well for IDE resolution let src = self.expander.in_file(syntax_ptr); self.source_map.expr_map.insert(src, inner); inner @@ -707,6 +696,7 @@ impl ExprCollector<'_> { .alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) }); let old_label = self.current_try_block_label.replace(label); + let ptr = AstPtr::new(&e).upcast(); let (btail, expr_id) = self.with_labeled_rib(label, |this| { let mut btail = None; let block = this.collect_block_(e, |id, statements, tail| { @@ -716,23 +706,21 @@ impl ExprCollector<'_> { (btail, block) }); - let callee = self.alloc_expr_desugared(Expr::Path(try_from_output)); + let callee = self.alloc_expr_desugared_with_ptr(Expr::Path(try_from_output), ptr); let next_tail = match btail { - Some(tail) => self.alloc_expr_desugared(Expr::Call { - callee, - args: Box::new([tail]), - is_assignee_expr: false, - }), + Some(tail) => self.alloc_expr_desugared_with_ptr( + Expr::Call { callee, args: Box::new([tail]), is_assignee_expr: false }, + ptr, + ), None => { - let unit = self.alloc_expr_desugared(Expr::Tuple { - exprs: Box::new([]), - is_assignee_expr: false, - }); - self.alloc_expr_desugared(Expr::Call { - callee, - args: Box::new([unit]), - is_assignee_expr: false, - }) + let unit = self.alloc_expr_desugared_with_ptr( + Expr::Tuple { exprs: Box::new([]), is_assignee_expr: false }, + ptr, + ); + self.alloc_expr_desugared_with_ptr( + Expr::Call { callee, args: Box::new([unit]), is_assignee_expr: false }, + ptr, + ) } }; let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else { @@ -1067,16 +1055,12 @@ impl ExprCollector<'_> { None => None, }, ); - match expansion { - Some(tail) => { - // Make the macro-call point to its expanded expression so we can query - // semantics on syntax pointers to the macro - let src = self.expander.in_file(syntax_ptr); - self.source_map.expr_map.insert(src, tail); - Some(tail) - } - None => None, - } + expansion.inspect(|&tail| { + // Make the macro-call point to its expanded expression so we can query + // semantics on syntax pointers to the macro + let src = self.expander.in_file(syntax_ptr); + self.source_map.expr_map.insert(src, tail); + }) } fn collect_stmt(&mut self, statements: &mut Vec, s: ast::Stmt) { @@ -1261,7 +1245,7 @@ impl ExprCollector<'_> { (Some(id), Pat::Bind { id, subpat }) }; - let ptr = AstPtr::new(&Either::Left(pat)); + let ptr = AstPtr::new(&pat); let pat = self.alloc_pat(pattern, ptr); if let Some(binding_id) = binding { self.add_definition_to_binding(binding_id, pat); @@ -1359,9 +1343,10 @@ impl ExprCollector<'_> { suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(), } } - #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676 ast::Pat::LiteralPat(lit) => 'b: { - let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing }; + let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { + break 'b Pat::Missing; + }; let expr = Expr::Literal(hir_lit); let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); let expr_id = self.alloc_expr(expr, expr_ptr); @@ -1397,7 +1382,7 @@ impl ExprCollector<'_> { ast::Pat::MacroPat(mac) => match mac.macro_call() { Some(call) => { let macro_ptr = AstPtr::new(&call); - let src = self.expander.in_file(AstPtr::new(&Either::Left(pat))); + let src = self.expander.in_file(AstPtr::new(&pat)); let pat = self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { this.collect_pat_opt(expanded_pat, binding_list) @@ -1426,7 +1411,7 @@ impl ExprCollector<'_> { Pat::Range { start, end } } }; - let ptr = AstPtr::new(&Either::Left(pat)); + let ptr = AstPtr::new(&pat); self.alloc_pat(pattern, ptr) } @@ -1987,10 +1972,19 @@ impl ExprCollector<'_> { self.source_map.expr_map.insert(src, id); id } - // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow. + // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed. + // Migrate to alloc_expr_desugared_with_ptr and then rename back fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { self.body.exprs.alloc(expr) } + fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { + let src = self.expander.in_file(ptr); + let id = self.body.exprs.alloc(expr); + self.source_map.expr_map_back.insert(id, src); + // We intentionally don't fill this as it could overwrite a non-desugared entry + // self.source_map.expr_map.insert(src, id); + id + } fn missing_expr(&mut self) -> ExprId { self.alloc_expr_desugared(Expr::Missing) } diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index b2aab55a6a89..cbb5ca887f49 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -48,7 +48,16 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); - body.params.iter().zip(db.function_data(it).params.iter()).for_each(|(¶m, ty)| { + let params = &db.function_data(it).params; + let mut params = params.iter(); + if let Some(self_param) = body.self_param { + p.print_binding(self_param); + p.buf.push(':'); + if let Some(ty) = params.next() { + p.print_type_ref(ty); + } + } + body.params.iter().zip(params).for_each(|(¶m, ty)| { p.print_pat(param); p.buf.push(':'); p.print_type_ref(ty); diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index 69b82ae871a4..0020e4eac307 100644 --- a/crates/hir-def/src/body/scope.rs +++ b/crates/hir-def/src/body/scope.rs @@ -96,6 +96,9 @@ impl ExprScopes { scope_by_expr: ArenaMap::with_capacity(body.exprs.len()), }; let mut root = scopes.root_scope(); + if let Some(self_param) = body.self_param { + scopes.add_bindings(body, root, self_param); + } scopes.add_params_bindings(body, root, &body.params); compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root); scopes diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs index f1c6b3b89fc5..0b41984bdd89 100644 --- a/crates/hir-def/src/child_by_source.rs +++ b/crates/hir-def/src/child_by_source.rs @@ -76,7 +76,7 @@ impl ChildBySource for ItemScope { self.extern_crate_decls() .for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::EXTERN_CRATE)); self.use_decls().for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::USE)); - self.unnamed_consts(db) + self.unnamed_consts() .for_each(|konst| insert_item_loc(db, res, file_id, konst, keys::CONST)); self.attr_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each( |(ast_id, call_id)| { diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index d4c1db8b95b4..b815c9b73ef8 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -715,7 +715,7 @@ impl<'a> AssocItemCollector<'a> { } AssocItem::MacroCall(call) => { let file_id = self.expander.current_file_id(); - let MacroCall { ast_id, expand_to, call_site, ref path } = item_tree[call]; + let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call]; let module = self.expander.module.local_id; let resolver = |path| { @@ -734,7 +734,7 @@ impl<'a> AssocItemCollector<'a> { match macro_call_as_call_id( self.db.upcast(), &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)), - call_site, + ctxt, expand_to, self.expander.module.krate(), resolver, @@ -745,6 +745,7 @@ impl<'a> AssocItemCollector<'a> { self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike { ast_id: InFile::new(file_id, ast_id), expand_to: hir_expand::ExpandTo::Items, + eager: None, }); } Ok(None) => (), @@ -754,6 +755,7 @@ impl<'a> AssocItemCollector<'a> { MacroCallKind::FnLike { ast_id: InFile::new(file_id, ast_id), expand_to, + eager: None, }, Clone::clone(path), )); diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 5790e600f63c..a7461b78af1c 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -191,9 +191,9 @@ impl StructData { let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); - let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); + let cfg_options = db.crate_graph()[krate].cfg_options.clone(); - let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); + let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { @@ -248,9 +248,9 @@ impl StructData { let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); - let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); + let cfg_options = db.crate_graph()[krate].cfg_options.clone(); - let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); + let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 544ed6bc347d..30d52d87f192 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -309,13 +309,9 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()), local_inner: false, allow_internal_unsafe: loc.allow_internal_unsafe, - span: db - .span_map(loc.id.file_id()) - .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), edition: loc.edition, } } - MacroId::MacroRulesId(it) => { let loc: MacroRulesLoc = it.lookup(db); @@ -328,9 +324,6 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { allow_internal_unsafe: loc .flags .contains(MacroRulesLocFlags::ALLOW_INTERNAL_UNSAFE), - span: db - .span_map(loc.id.file_id()) - .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), edition: loc.edition, } } @@ -348,9 +341,6 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { ), local_inner: false, allow_internal_unsafe: false, - span: db - .span_map(loc.id.file_id()) - .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), edition: loc.edition, } } diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 0e6826a75a6c..2b059d1f8dcd 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -241,30 +241,8 @@ impl ItemScope { }) } - pub fn unnamed_consts<'a>( - &'a self, - db: &'a dyn DefDatabase, - ) -> impl Iterator + 'a { - // FIXME: Also treat consts named `_DERIVE_*` as unnamed, since synstructure generates those. - // Should be removed once synstructure stops doing that. - let synstructure_hack_consts = self.values.values().filter_map(|(item, _, _)| match item { - &ModuleDefId::ConstId(id) => { - let loc = id.lookup(db); - let item_tree = loc.id.item_tree(db); - if item_tree[loc.id.value] - .name - .as_ref() - .map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_")) - { - Some(id) - } else { - None - } - } - _ => None, - }); - - self.unnamed_consts.iter().copied().chain(synstructure_hack_consts) + pub fn unnamed_consts(&self) -> impl Iterator + '_ { + self.unnamed_consts.iter().copied() } /// Iterate over all module scoped macros diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index bd3d377ec083..585e93ce21e1 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -49,7 +49,7 @@ use intern::Interned; use la_arena::{Arena, Idx, IdxRange, RawIdx}; use rustc_hash::FxHashMap; use smallvec::SmallVec; -use span::{AstIdNode, FileAstId, Span}; +use span::{AstIdNode, FileAstId, SyntaxContextId}; use stdx::never; use syntax::{ast, match_ast, SyntaxKind}; use triomphe::Arc; @@ -790,8 +790,7 @@ pub struct MacroCall { pub path: Interned, pub ast_id: FileAstId, pub expand_to: ExpandTo, - // FIXME: We need to move this out. It invalidates the item tree when typing inside the macro call. - pub call_site: Span, + pub ctxt: SyntaxContextId, } #[derive(Debug, Clone, Eq, PartialEq)] diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index bf3d54f4caf4..f02163cbe44f 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -560,35 +560,32 @@ impl<'a> Ctx<'a> { fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option> { let span_map = self.span_map(); - let path = Interned::new(ModPath::from_src(self.db.upcast(), m.path()?, &mut |range| { + let path = m.path()?; + let range = path.syntax().text_range(); + let path = Interned::new(ModPath::from_src(self.db.upcast(), path, &mut |range| { span_map.span_for_range(range).ctx })?); let ast_id = self.source_ast_id_map.ast_id(m); let expand_to = hir_expand::ExpandTo::from_call_site(m); - let res = MacroCall { - path, - ast_id, - expand_to, - call_site: span_map.span_for_range(m.syntax().text_range()), - }; + let res = MacroCall { path, ast_id, expand_to, ctxt: span_map.span_for_range(range).ctx }; Some(id(self.data().macro_calls.alloc(res))) } fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option> { - let name = m.name().map(|it| it.as_name())?; + let name = m.name()?; let ast_id = self.source_ast_id_map.ast_id(m); - let res = MacroRules { name, ast_id }; + let res = MacroRules { name: name.as_name(), ast_id }; Some(id(self.data().macro_rules.alloc(res))) } fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option> { - let name = m.name().map(|it| it.as_name())?; + let name = m.name()?; let ast_id = self.source_ast_id_map.ast_id(m); let visibility = self.lower_visibility(m); - let res = Macro2 { name, ast_id, visibility }; + let res = Macro2 { name: name.as_name(), ast_id, visibility }; Some(id(self.data().macro_defs.alloc(res))) } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 87c90a4c6ab9..953bf6b85d64 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -487,12 +487,12 @@ impl Printer<'_> { } } ModItem::MacroCall(it) => { - let MacroCall { path, ast_id, expand_to, call_site } = &self.tree[it]; + let MacroCall { path, ast_id, expand_to, ctxt } = &self.tree[it]; let _ = writeln!( self, - "// AstId: {:?}, Span: {}, ExpandTo: {:?}", + "// AstId: {:?}, SyntaxContext: {}, ExpandTo: {:?}", ast_id.erase().into_raw(), - call_site, + ctxt, expand_to ); wln!(self, "{}!(...);", path.display(self.db.upcast())); diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 26f7b41c77a1..48da876ac158 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -278,7 +278,7 @@ m!(); // AstId: 2 pub macro m2 { ... } - // AstId: 3, Span: 0:3@0..5#0, ExpandTo: Items + // AstId: 3, SyntaxContext: 0, ExpandTo: Items m!(...); "#]], ); diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index d63f2268aa4c..828842de7e8a 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -90,7 +90,7 @@ use hir_expand::{ use item_tree::ExternBlock; use la_arena::Idx; use nameres::DefMap; -use span::{AstIdNode, FileAstId, FileId, Span}; +use span::{AstIdNode, FileAstId, FileId, SyntaxContextId}; use stdx::impl_from; use syntax::{ast, AstNode}; @@ -1342,21 +1342,22 @@ impl AsMacroCall for InFile<&ast::MacroCall> { let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); let span_map = db.span_map(self.file_id); let path = self.value.path().and_then(|path| { - path::ModPath::from_src(db, path, &mut |range| { + let range = path.syntax().text_range(); + let mod_path = path::ModPath::from_src(db, path, &mut |range| { span_map.as_ref().span_for_range(range).ctx - }) + })?; + let call_site = span_map.span_for_range(range); + Some((call_site, mod_path)) }); - let Some(path) = path else { + let Some((call_site, path)) = path else { return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation"))); }; - let call_site = span_map.span_for_range(self.value.syntax().text_range()); - macro_call_as_call_id_with_eager( db, &AstIdWithPath::new(ast_id.file_id, ast_id.value, path), - call_site, + call_site.ctx, expands_to, krate, resolver, @@ -1381,7 +1382,7 @@ impl AstIdWithPath { fn macro_call_as_call_id( db: &dyn ExpandDatabase, call: &AstIdWithPath, - call_site: Span, + call_site: SyntaxContextId, expand_to: ExpandTo, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option + Copy, @@ -1393,7 +1394,7 @@ fn macro_call_as_call_id( fn macro_call_as_call_id_with_eager( db: &dyn ExpandDatabase, call: &AstIdWithPath, - call_site: Span, + call_site: SyntaxContextId, expand_to: ExpandTo, krate: CrateId, resolver: impl FnOnce(path::ModPath) -> Option, @@ -1403,17 +1404,20 @@ fn macro_call_as_call_id_with_eager( resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?; let res = match def.kind { - MacroDefKind::BuiltInEager(..) => { - let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db)); - expand_eager_macro_input(db, krate, macro_call, def, call_site, &|path| { - eager_resolver(path).filter(MacroDefId::is_fn_like) - }) - } + MacroDefKind::BuiltInEager(..) => expand_eager_macro_input( + db, + krate, + &call.ast_id.to_node(db), + call.ast_id, + def, + call_site, + &|path| eager_resolver(path).filter(MacroDefId::is_fn_like), + ), _ if def.is_fn_like() => ExpandResult { - value: Some(def.as_lazy_macro( + value: Some(def.make_call( db, krate, - MacroCallKind::FnLike { ast_id: call.ast_id, expand_to }, + MacroCallKind::FnLike { ast_id: call.ast_id, expand_to, eager: None }, call_site, )), err: None, diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index 86b4466153a3..89c1b446081c 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -528,3 +528,121 @@ impl < > $crate::fmt::Debug for Command< > where { }"#]], ); } +#[test] +fn test_debug_expand_with_cfg() { + check( + r#" + //- minicore: derive, fmt + use core::fmt::Debug; + + #[derive(Debug)] + struct HideAndShow { + #[cfg(never)] + always_hide: u32, + #[cfg(not(never))] + always_show: u32, + } + #[derive(Debug)] + enum HideAndShowEnum { + #[cfg(never)] + AlwaysHide, + #[cfg(not(never))] + AlwaysShow{ + #[cfg(never)] + always_hide: u32, + #[cfg(not(never))] + always_show: u32, + } + } + "#, + expect![[r#" +use core::fmt::Debug; + +#[derive(Debug)] +struct HideAndShow { + #[cfg(never)] + always_hide: u32, + #[cfg(not(never))] + always_show: u32, +} +#[derive(Debug)] +enum HideAndShowEnum { + #[cfg(never)] + AlwaysHide, + #[cfg(not(never))] + AlwaysShow{ + #[cfg(never)] + always_hide: u32, + #[cfg(not(never))] + always_show: u32, + } +} + +impl < > $crate::fmt::Debug for HideAndShow< > where { + fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { + match self { + HideAndShow { + always_show: always_show, + } + =>f.debug_struct("HideAndShow").field("always_show", &always_show).finish() + } + } +} +impl < > $crate::fmt::Debug for HideAndShowEnum< > where { + fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { + match self { + HideAndShowEnum::AlwaysShow { + always_show: always_show, + } + =>f.debug_struct("AlwaysShow").field("always_show", &always_show).finish(), + } + } +}"#]], + ); +} +#[test] +fn test_default_expand_with_cfg() { + check( + r#" +//- minicore: derive, default +#[derive(Default)] +struct Foo { + field1: i32, + #[cfg(never)] + field2: (), +} +#[derive(Default)] +enum Bar { + Foo, + #[cfg_attr(not(never), default)] + Bar, +} +"#, + expect![[r#" +#[derive(Default)] +struct Foo { + field1: i32, + #[cfg(never)] + field2: (), +} +#[derive(Default)] +enum Bar { + Foo, + #[cfg_attr(not(never), default)] + Bar, +} + +impl < > $crate::default::Default for Foo< > where { + fn default() -> Self { + Foo { + field1: $crate::default::Default::default(), + } + } +} +impl < > $crate::default::Default for Bar< > where { + fn default() -> Self { + Bar::Bar + } +}"#]], + ); +} diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index edc8247f166d..965f329acb9e 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -171,7 +171,7 @@ fn main(foo: ()) { } fn main(foo: ()) { - /* error: unresolved macro unresolved */"helloworld!"#0:3@207..323#2#; + /* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#0#; } } diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs index 63f211022c97..23d8b023b8bb 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs @@ -33,7 +33,7 @@ m!(&k"); "#, expect![[r#" macro_rules! m { ($i:literal) => {}; } -/* error: mismatched delimiters */"#]], +/* error: expected literal */"#]], ); } diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs index 362c189f6a73..fb5797d6e532 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs @@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } } macro_rules! m2 { ($x:ident) => {} } /* error: macro definition has parse errors */ -/* error: mismatched delimiters */ +/* error: expected ident */ "#]], ) } diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 764617eafb7b..b56dee3efb55 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -61,15 +61,16 @@ use std::ops::Deref; use base_db::{CrateId, Edition, FileId}; use hir_expand::{ - name::Name, proc_macro::ProcMacroKind, HirFileId, InFile, MacroCallId, MacroDefId, + name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, }; use itertools::Itertools; use la_arena::Arena; use rustc_hash::{FxHashMap, FxHashSet}; -use span::FileAstId; +use span::{FileAstId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; use syntax::{ast, SmolStr}; use triomphe::Arc; +use tt::TextRange; use crate::{ db::DefDatabase, @@ -677,6 +678,25 @@ impl ModuleData { } } + pub fn definition_source_range(&self, db: &dyn DefDatabase) -> InFile { + match &self.origin { + &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => { + InFile::new( + definition.into(), + ErasedAstId::new(definition.into(), ROOT_ERASED_FILE_AST_ID) + .to_range(db.upcast()), + ) + } + &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new( + definition_tree_id.file_id(), + AstId::new(definition_tree_id.file_id(), definition).to_range(db.upcast()), + ), + ModuleOrigin::BlockExpr { block, .. } => { + InFile::new(block.file_id, block.to_range(db.upcast())) + } + } + } + /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. /// `None` for the crate root or block. pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option> { @@ -684,6 +704,13 @@ impl ModuleData { let value = decl.to_node(db.upcast()); Some(InFile { file_id: decl.file_id, value }) } + + /// Returns the range which declares this module, either a `mod foo;` or a `mod foo {}`. + /// `None` for the crate root or block. + pub fn declaration_source_range(&self, db: &dyn DefDatabase) -> Option> { + let decl = self.origin.declaration()?; + Some(InFile { file_id: decl.file_id, value: decl.to_range(db.upcast()) }) + } } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs index 1cadae8c87c4..662c80edf324 100644 --- a/crates/hir-def/src/nameres/attr_resolution.rs +++ b/crates/hir-def/src/nameres/attr_resolution.rs @@ -5,7 +5,7 @@ use hir_expand::{ attrs::{Attr, AttrId, AttrInput}, MacroCallId, MacroCallKind, MacroDefId, }; -use span::Span; +use span::SyntaxContextId; use syntax::{ast, SmolStr}; use triomphe::Arc; @@ -109,14 +109,14 @@ pub(super) fn attr_macro_as_call_id( let arg = match macro_attr.input.as_deref() { Some(AttrInput::TokenTree(tt)) => { let mut tt = tt.as_ref().clone(); - tt.delimiter = tt::Delimiter::invisible_spanned(macro_attr.span); + tt.delimiter.kind = tt::DelimiterKind::Invisible; Some(tt) } _ => None, }; - def.as_lazy_macro( + def.make_call( db.upcast(), krate, MacroCallKind::Attr { @@ -124,7 +124,7 @@ pub(super) fn attr_macro_as_call_id( attr_args: arg.map(Arc::new), invoc_attr_index: macro_attr.id, }, - macro_attr.span, + macro_attr.ctxt, ) } @@ -133,14 +133,14 @@ pub(super) fn derive_macro_as_call_id( item_attr: &AstIdWithPath, derive_attr_index: AttrId, derive_pos: u32, - call_site: Span, + call_site: SyntaxContextId, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { let (macro_id, def_id) = resolver(item_attr.path.clone()) .filter(|(_, def_id)| def_id.is_derive()) .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; - let call_id = def_id.as_lazy_macro( + let call_id = def_id.make_call( db.upcast(), krate, MacroCallKind::Derive { diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index f9fe6d3b903b..3d026447fb74 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -230,13 +230,13 @@ enum MacroDirectiveKind { FnLike { ast_id: AstIdWithPath, expand_to: ExpandTo, - call_site: Span, + ctxt: SyntaxContextId, }, Derive { ast_id: AstIdWithPath, derive_attr: AttrId, derive_pos: usize, - call_site: Span, + ctxt: SyntaxContextId, }, Attr { ast_id: AstIdWithPath, @@ -1126,7 +1126,7 @@ impl DefCollector<'_> { let resolver_def_id = |path| resolver(path).map(|(_, it)| it); match &directive.kind { - MacroDirectiveKind::FnLike { ast_id, expand_to, call_site } => { + MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => { let call_id = macro_call_as_call_id( self.db.upcast(), ast_id, @@ -1146,7 +1146,7 @@ impl DefCollector<'_> { return Resolved::Yes; } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site } => { + MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => { let id = derive_macro_as_call_id( self.db, ast_id, @@ -1266,7 +1266,7 @@ impl DefCollector<'_> { ast_id, derive_attr: attr.id, derive_pos: idx, - call_site, + ctxt: call_site.ctx, }, container: directive.container, }); @@ -1428,7 +1428,7 @@ impl DefCollector<'_> { for directive in &self.unresolved_macros { match &directive.kind { - MacroDirectiveKind::FnLike { ast_id, expand_to, call_site } => { + MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => { // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error! let macro_call_as_call_id = macro_call_as_call_id( self.db.upcast(), @@ -1451,12 +1451,16 @@ impl DefCollector<'_> { if let Err(UnresolvedMacro { path }) = macro_call_as_call_id { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id, - MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: *expand_to }, + MacroCallKind::FnLike { + ast_id: ast_id.ast_id, + expand_to: *expand_to, + eager: None, + }, path, )); } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site: _ } => { + MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id, MacroCallKind::Derive { @@ -2285,7 +2289,7 @@ impl ModCollector<'_, '_> { fn collect_macro_call( &mut self, - &MacroCall { ref path, ast_id, expand_to, call_site }: &MacroCall, + &MacroCall { ref path, ast_id, expand_to, ctxt }: &MacroCall, container: ItemContainerId, ) { let ast_id = AstIdWithPath::new(self.file_id(), ast_id, ModPath::clone(path)); @@ -2299,7 +2303,7 @@ impl ModCollector<'_, '_> { if let Ok(res) = macro_call_as_call_id_with_eager( db.upcast(), &ast_id, - call_site, + ctxt, expand_to, self.def_collector.def_map.krate, |path| { @@ -2357,7 +2361,7 @@ impl ModCollector<'_, '_> { self.def_collector.unresolved_macros.push(MacroDirective { module_id: self.module_id, depth: self.macro_depth + 1, - kind: MacroDirectiveKind::FnLike { ast_id, expand_to, call_site }, + kind: MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt }, container, }); } diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs index 6efced02718a..be41634eb578 100644 --- a/crates/hir-def/src/nameres/tests/incremental.rs +++ b/crates/hir-def/src/nameres/tests/incremental.rs @@ -1,6 +1,5 @@ -use base_db::{SourceDatabase, SourceDatabaseExt}; +use base_db::{SourceDatabase, SourceDatabaseExt2 as _}; use test_fixture::WithFixture; -use triomphe::Arc; use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId}; @@ -17,7 +16,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: }); assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}") } - db.set_file_text(pos.file_id, Arc::from(ra_fixture_change)); + db.set_file_text(pos.file_id, ra_fixture_change); { let events = db.log_executed(|| { @@ -267,7 +266,7 @@ fn quux() { 92 } m!(Y); m!(Z); "#; - db.set_file_text(pos.file_id, Arc::from(new_text)); + db.set_file_text(pos.file_id, new_text); { let events = db.log_executed(|| { diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs index 4283f003f897..2b1da8c34e15 100644 --- a/crates/hir-def/src/src.rs +++ b/crates/hir-def/src/src.rs @@ -3,7 +3,7 @@ use either::Either; use hir_expand::InFile; use la_arena::ArenaMap; -use syntax::ast; +use syntax::{ast, AstNode, AstPtr}; use crate::{ data::adt::lower_struct, db::DefDatabase, item_tree::ItemTreeNode, trace::Trace, GenericDefId, @@ -12,8 +12,12 @@ use crate::{ }; pub trait HasSource { - type Value; - fn source(&self, db: &dyn DefDatabase) -> InFile; + type Value: AstNode; + fn source(&self, db: &dyn DefDatabase) -> InFile { + let InFile { file_id, value } = self.ast_ptr(db); + InFile::new(file_id, value.to_node(&db.parse_or_expand(file_id))) + } + fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile>; } impl HasSource for T @@ -22,16 +26,14 @@ where T::Id: ItemTreeNode, { type Value = ::Source; - - fn source(&self, db: &dyn DefDatabase) -> InFile { + fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile> { let id = self.item_tree_id(); let file_id = id.file_id(); let tree = id.item_tree(db); let ast_id_map = db.ast_id_map(file_id); - let root = db.parse_or_expand(file_id); let node = &tree[id.value]; - InFile::new(file_id, ast_id_map.get(node.ast_id()).to_node(&root)) + InFile::new(file_id, ast_id_map.get(node.ast_id())) } } diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index 7793e9953231..af3ecdcd5e32 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -7,7 +7,7 @@ use either::Either; use intern::Interned; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; -use span::Span; +use span::{Span, SyntaxContextId}; use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; use triomphe::Arc; @@ -53,7 +53,7 @@ impl RawAttrs { id, input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), path: Interned::new(ModPath::from(crate::name!(doc))), - span: span_map.span_for_range(comment.syntax().text_range()), + ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx, }), }); let entries: Arc<[Attr]> = Arc::from_iter(entries); @@ -173,7 +173,7 @@ pub struct Attr { pub id: AttrId, pub path: Interned, pub input: Option>, - pub span: Span, + pub ctxt: SyntaxContextId, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -201,10 +201,12 @@ impl Attr { span_map: SpanMapRef<'_>, id: AttrId, ) -> Option { - let path = Interned::new(ModPath::from_src(db, ast.path()?, &mut |range| { + let path = ast.path()?; + let range = path.syntax().text_range(); + let path = Interned::new(ModPath::from_src(db, path, &mut |range| { span_map.span_for_range(range).ctx })?); - let span = span_map.span_for_range(ast.syntax().text_range()); + let span = span_map.span_for_range(range); let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { let value = match lit.kind() { ast::LiteralKind::String(string) => string.value()?.into(), @@ -217,11 +219,11 @@ impl Attr { } else { None }; - Some(Attr { id, path, input, span }) + Some(Attr { id, path, input, ctxt: span.ctx }) } fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree], id: AttrId) -> Option { - let span = tt.first()?.first_span(); + let ctxt = tt.first()?.first_span().ctx; let path_end = tt .iter() .position(|tt| { @@ -253,7 +255,7 @@ impl Attr { } _ => None, }; - Some(Attr { id, path, input, span }) + Some(Attr { id, path, input, ctxt }) } pub fn path(&self) -> &ModPath { diff --git a/crates/hir-expand/src/builtin_attr_macro.rs b/crates/hir-expand/src/builtin_attr_macro.rs index a0102f36aff5..9ff29b484d32 100644 --- a/crates/hir-expand/src/builtin_attr_macro.rs +++ b/crates/hir-expand/src/builtin_attr_macro.rs @@ -11,7 +11,7 @@ macro_rules! register_builtin { } impl BuiltinAttrExpander { - pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree) -> ExpandResult { + pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult { match *self { $( BuiltinAttrExpander::$variant => $expand, )* } @@ -34,8 +34,9 @@ impl BuiltinAttrExpander { db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { - self.expander()(db, id, tt) + self.expander()(db, id, tt, span) } pub fn is_derive(self) -> bool { @@ -71,6 +72,7 @@ fn dummy_attr_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, + _span: Span, ) -> ExpandResult { ExpandResult::ok(tt.clone()) } @@ -100,6 +102,7 @@ fn derive_expand( db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { let loc = db.lookup_intern_macro_call(id); let derives = match &loc.kind { @@ -107,17 +110,14 @@ fn derive_expand( attr_args } _ => { - return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { - open: loc.call_site, - close: loc.call_site, - })) + return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { open: span, close: span })) } }; - pseudo_derive_attr_expansion(tt, derives, loc.call_site) + pseudo_derive_attr_expansion(tt, derives, span) } pub fn pseudo_derive_attr_expansion( - tt: &tt::Subtree, + _: &tt::Subtree, args: &tt::Subtree, call_site: Span, ) -> ExpandResult { @@ -141,7 +141,7 @@ pub fn pseudo_derive_attr_expansion( token_trees.push(mk_leaf(']')); } ExpandResult::ok(tt::Subtree { - delimiter: tt.delimiter, + delimiter: args.delimiter, token_trees: token_trees.into_boxed_slice(), }) } diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 66dec7d89e5a..528038a9ccfa 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -50,8 +50,8 @@ impl BuiltinDeriveExpander { db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { - let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); self.expander()(span, tt) } diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 0fd0c25dcce2..9fb6a0b2346b 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -19,14 +19,14 @@ use crate::{ }; macro_rules! register_builtin { - ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { + ( $LAZY:ident: $(($name:ident, $kind: ident) => $expand:ident),* , $EAGER:ident: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub enum BuiltinFnLikeExpander { + pub enum $LAZY { $($kind),* } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub enum EagerExpander { + pub enum $EAGER { $($e_kind),* } @@ -62,8 +62,8 @@ impl BuiltinFnLikeExpander { db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { - let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); self.expander()(db, id, tt, span) } @@ -75,8 +75,8 @@ impl EagerExpander { db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { - let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); self.expander()(db, id, tt, span) } @@ -84,6 +84,17 @@ impl EagerExpander { pub fn is_include(&self) -> bool { matches!(self, EagerExpander::Include) } + + pub fn is_include_like(&self) -> bool { + matches!( + self, + EagerExpander::Include | EagerExpander::IncludeStr | EagerExpander::IncludeBytes + ) + } + + pub fn is_env_or_option_env(&self) -> bool { + matches!(self, EagerExpander::Env | EagerExpander::OptionEnv) + } } pub fn find_builtin_macro( @@ -93,7 +104,7 @@ pub fn find_builtin_macro( } register_builtin! { - LAZY: + BuiltinFnLikeExpander: (column, Column) => line_expand, (file, File) => file_expand, (line, Line) => line_expand, @@ -114,7 +125,7 @@ register_builtin! { (format_args_nl, FormatArgsNl) => format_args_nl_expand, (quote, Quote) => quote_expand, - EAGER: + EagerExpander: (compile_error, CompileError) => compile_error_expand, (concat, Concat) => concat_expand, (concat_idents, ConcatIdents) => concat_idents_expand, @@ -426,22 +437,25 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { } } -fn unquote_str(lit: &tt::Literal) -> Option { +fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> { + let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::String::cast(lit)?; - token.value().map(|it| it.into_owned()) + token.value().map(|it| (it.into_owned(), span)) } -fn unquote_char(lit: &tt::Literal) -> Option { +fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> { + let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::Char::cast(lit)?; - token.value() + token.value().zip(Some(span)) } -fn unquote_byte_string(lit: &tt::Literal) -> Option> { +fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec, Span)> { + let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::ByteString::cast(lit)?; - token.value().map(|it| it.into_owned()) + token.value().map(|it| (it.into_owned(), span)) } fn compile_error_expand( @@ -452,7 +466,7 @@ fn compile_error_expand( ) -> ExpandResult { let err = match &*tt.token_trees { [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) { - Some(unquoted) => ExpandError::other(unquoted.into_boxed_str()), + Some((unquoted, _)) => ExpandError::other(unquoted.into_boxed_str()), None => ExpandError::other("`compile_error!` argument must be a string"), }, _ => ExpandError::other("`compile_error!` argument must be a string"), @@ -465,10 +479,16 @@ fn concat_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, - span: Span, + _: Span, ) -> ExpandResult { let mut err = None; let mut text = String::new(); + let mut span: Option = None; + let mut record_span = |s: Span| match &mut span { + Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range), + Some(_) => (), + None => span = Some(s), + }; for (i, mut t) in tt.token_trees.iter().enumerate() { // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses // to ensure the right parsing order, so skip the parentheses here. Ideally we'd @@ -486,11 +506,14 @@ fn concat_expand( // concat works with string and char literals, so remove any quotes. // It also works with integer, float and boolean literals, so just use the rest // as-is. - if let Some(c) = unquote_char(it) { + if let Some((c, span)) = unquote_char(it) { text.push(c); + record_span(span); } else { - let component = unquote_str(it).unwrap_or_else(|| it.text.to_string()); + let (component, span) = + unquote_str(it).unwrap_or_else(|| (it.text.to_string(), it.span)); text.push_str(&component); + record_span(span); } } // handle boolean literals @@ -498,6 +521,7 @@ fn concat_expand( if i % 2 == 0 && (id.text == "true" || id.text == "false") => { text.push_str(id.text.as_str()); + record_span(id.span); } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { @@ -505,6 +529,7 @@ fn concat_expand( } } } + let span = span.unwrap_or(tt.delimiter.open); ExpandResult { value: quote!(span =>#text), err } } @@ -512,18 +537,25 @@ fn concat_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, - span: Span, + call_site: Span, ) -> ExpandResult { let mut bytes = Vec::new(); let mut err = None; + let mut span: Option = None; + let mut record_span = |s: Span| match &mut span { + Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range), + Some(_) => (), + None => span = Some(s), + }; for (i, t) in tt.token_trees.iter().enumerate() { match t { tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { let token = ast::make::tokens::literal(&lit.to_string()); + record_span(lit.span); match token.kind() { syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()), syntax::SyntaxKind::BYTE_STRING => { - let components = unquote_byte_string(lit).unwrap_or_default(); + let components = unquote_byte_string(lit).map_or(vec![], |(it, _)| it); components.into_iter().for_each(|it| bytes.push(it.to_string())); } _ => { @@ -534,7 +566,7 @@ fn concat_bytes_expand( } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => { - if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes) { + if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span) { err.get_or_insert(e); break; } @@ -546,17 +578,24 @@ fn concat_bytes_expand( } } let value = tt::Subtree { - delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket }, + delimiter: tt::Delimiter { + open: call_site, + close: call_site, + kind: tt::DelimiterKind::Bracket, + }, token_trees: { Itertools::intersperse_with( bytes.into_iter().map(|it| { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: it.into(), span })) + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text: it.into(), + span: span.unwrap_or(call_site), + })) }), || { tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', spacing: tt::Spacing::Alone, - span, + span: call_site, })) }, ) @@ -569,13 +608,15 @@ fn concat_bytes_expand( fn concat_bytes_expand_subtree( tree: &tt::Subtree, bytes: &mut Vec, + mut record_span: impl FnMut(Span), ) -> Result<(), ExpandError> { for (ti, tt) in tree.token_trees.iter().enumerate() { match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - let lit = ast::make::tokens::literal(&lit.to_string()); + tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => { + let lit = ast::make::tokens::literal(&it.to_string()); match lit.kind() { syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => { + record_span(it.span); bytes.push(lit.text().to_owned()) } _ => { @@ -635,7 +676,7 @@ fn relative_file( } } -fn parse_string(tt: &tt::Subtree) -> Result { +fn parse_string(tt: &tt::Subtree) -> Result<(String, Span), ExpandError> { tt.token_trees .first() .and_then(|tt| match tt { @@ -675,7 +716,7 @@ pub fn include_input_to_file_id( arg_id: MacroCallId, arg: &tt::Subtree, ) -> Result { - relative_file(db, arg_id, &parse_string(arg)?, false) + relative_file(db, arg_id, &parse_string(arg)?.0, false) } fn include_bytes_expand( @@ -701,7 +742,7 @@ fn include_str_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - let path = match parse_string(tt) { + let (path, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) @@ -736,7 +777,7 @@ fn env_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - let key = match parse_string(tt) { + let (key, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) @@ -766,18 +807,24 @@ fn option_env_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, - span: Span, + call_site: Span, ) -> ExpandResult { - let key = match parse_string(tt) { + let (key, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) + return ExpandResult::new( + tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }), + e, + ) } }; - let dollar_crate = dollar_crate(span); + let dollar_crate = dollar_crate(call_site); let expanded = match get_env_inner(db, arg_id, &key) { - None => quote! {span => #dollar_crate::option::Option::None::<&str> }, - Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) }, + None => quote! {call_site => #dollar_crate::option::Option::None::<&str> }, + Some(s) => { + let s = quote! (span => #s); + quote! {call_site => #dollar_crate::option::Option::Some(#s) } + } }; ExpandResult::ok(expanded) diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs new file mode 100644 index 000000000000..c74c13a6fd3c --- /dev/null +++ b/crates/hir-expand/src/cfg_process.rs @@ -0,0 +1,327 @@ +//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro +use std::iter::Peekable; + +use cfg::{CfgAtom, CfgExpr}; +use rustc_hash::FxHashSet; +use syntax::{ + ast::{self, Attr, HasAttrs, Meta, VariantList}, + AstNode, NodeOrToken, SyntaxElement, SyntaxNode, T, +}; +use tracing::{debug, warn}; +use tt::SmolStr; + +use crate::{db::ExpandDatabase, MacroCallKind, MacroCallLoc}; + +fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { + if !attr.simple_name().as_deref().map(|v| v == "cfg")? { + return None; + } + debug!("Evaluating cfg {}", attr); + let cfg = parse_from_attr_meta(attr.meta()?)?; + debug!("Checking cfg {:?}", cfg); + let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg) != Some(false); + Some(enabled) +} + +fn check_cfg_attr_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { + if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? { + return None; + } + debug!("Evaluating cfg_attr {}", attr); + let cfg_expr = parse_from_attr_meta(attr.meta()?)?; + debug!("Checking cfg_attr {:?}", cfg_expr); + let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg_expr) != Some(false); + Some(enabled) +} + +fn process_has_attrs_with_possible_comma( + items: impl Iterator, + loc: &MacroCallLoc, + db: &dyn ExpandDatabase, + remove: &mut FxHashSet, +) -> Option<()> { + for item in items { + let field_attrs = item.attrs(); + 'attrs: for attr in field_attrs { + if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { + debug!("censoring type {:?}", item.syntax()); + remove.insert(item.syntax().clone().into()); + // We need to remove the , as well + remove_possible_comma(&item, remove); + break 'attrs; + } + + if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if enabled { + debug!("Removing cfg_attr tokens {:?}", attr); + let meta = attr.meta()?; + let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; + remove.extend(removes_from_cfg_attr); + } else { + debug!("censoring type cfg_attr {:?}", item.syntax()); + remove.insert(attr.syntax().clone().into()); + continue; + } + } + } + } + Some(()) +} +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum CfgExprStage { + /// Stripping the CFGExpr part of the attribute + StrippigCfgExpr, + /// Found the comma after the CFGExpr. Will keep all tokens until the next comma or the end of the attribute + FoundComma, + /// Everything following the attribute. This could be another attribute or the end of the attribute. + // FIXME: cfg_attr with multiple attributes will not be handled correctly. We will only keep the first attribute + // Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110 + EverythingElse, +} +/// This function creates its own set of tokens to remove. To help prevent malformed syntax as input. +fn remove_tokens_within_cfg_attr(meta: Meta) -> Option> { + let mut remove: FxHashSet = FxHashSet::default(); + debug!("Enabling attribute {}", meta); + let meta_path = meta.path()?; + debug!("Removing {:?}", meta_path.syntax()); + remove.insert(meta_path.syntax().clone().into()); + + let meta_tt = meta.token_tree()?; + debug!("meta_tt {}", meta_tt); + let mut stage = CfgExprStage::StrippigCfgExpr; + for tt in meta_tt.token_trees_and_tokens() { + debug!("Checking {:?}. Stage: {:?}", tt, stage); + match (stage, tt) { + (CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Node(node)) => { + remove.insert(node.syntax().clone().into()); + } + (CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Token(token)) => { + if token.kind() == T![,] { + stage = CfgExprStage::FoundComma; + } + remove.insert(token.into()); + } + (CfgExprStage::FoundComma, syntax::NodeOrToken::Token(token)) + if (token.kind() == T![,] || token.kind() == T![')']) => + { + // The end of the attribute or separator for the next attribute + stage = CfgExprStage::EverythingElse; + remove.insert(token.into()); + } + (CfgExprStage::EverythingElse, syntax::NodeOrToken::Node(node)) => { + remove.insert(node.syntax().clone().into()); + } + (CfgExprStage::EverythingElse, syntax::NodeOrToken::Token(token)) => { + remove.insert(token.into()); + } + // This is an actual attribute + _ => {} + } + } + if stage != CfgExprStage::EverythingElse { + warn!("Invalid cfg_attr attribute. {:?}", meta_tt); + return None; + } + Some(remove) +} +/// Removes a possible comma after the [AstNode] +fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet) { + if let Some(comma) = item.syntax().next_sibling_or_token().filter(|it| it.kind() == T![,]) { + res.insert(comma); + } +} +fn process_enum( + variants: VariantList, + loc: &MacroCallLoc, + db: &dyn ExpandDatabase, + remove: &mut FxHashSet, +) -> Option<()> { + 'variant: for variant in variants.variants() { + for attr in variant.attrs() { + if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { + // Rustc does not strip the attribute if it is enabled. So we will will leave it + debug!("censoring type {:?}", variant.syntax()); + remove.insert(variant.syntax().clone().into()); + // We need to remove the , as well + remove_possible_comma(&variant, remove); + continue 'variant; + }; + + if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if enabled { + debug!("Removing cfg_attr tokens {:?}", attr); + let meta = attr.meta()?; + let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; + remove.extend(removes_from_cfg_attr); + } else { + debug!("censoring type cfg_attr {:?}", variant.syntax()); + remove.insert(attr.syntax().clone().into()); + continue; + } + } + } + if let Some(fields) = variant.field_list() { + match fields { + ast::FieldList::RecordFieldList(fields) => { + process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?; + } + ast::FieldList::TupleFieldList(fields) => { + process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?; + } + } + } + } + Some(()) +} + +pub(crate) fn process_cfg_attrs( + node: &SyntaxNode, + loc: &MacroCallLoc, + db: &dyn ExpandDatabase, +) -> Option> { + // FIXME: #[cfg_eval] is not implemented. But it is not stable yet + if !matches!(loc.kind, MacroCallKind::Derive { .. }) { + return None; + } + let mut remove = FxHashSet::default(); + + let item = ast::Item::cast(node.clone())?; + for attr in item.attrs() { + if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if enabled { + debug!("Removing cfg_attr tokens {:?}", attr); + let meta = attr.meta()?; + let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; + remove.extend(removes_from_cfg_attr); + } else { + debug!("censoring type cfg_attr {:?}", item.syntax()); + remove.insert(attr.syntax().clone().into()); + continue; + } + } + } + match item { + ast::Item::Struct(it) => match it.field_list()? { + ast::FieldList::RecordFieldList(fields) => { + process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?; + } + ast::FieldList::TupleFieldList(fields) => { + process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?; + } + }, + ast::Item::Enum(it) => { + process_enum(it.variant_list()?, loc, db, &mut remove)?; + } + ast::Item::Union(it) => { + process_has_attrs_with_possible_comma( + it.record_field_list()?.fields(), + loc, + db, + &mut remove, + )?; + } + // FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now + _ => {} + } + Some(remove) +} +/// Parses a `cfg` attribute from the meta +fn parse_from_attr_meta(meta: Meta) -> Option { + let tt = meta.token_tree()?; + let mut iter = tt.token_trees_and_tokens().skip(1).peekable(); + next_cfg_expr_from_syntax(&mut iter) +} + +fn next_cfg_expr_from_syntax(iter: &mut Peekable) -> Option +where + I: Iterator>, +{ + let name = match iter.next() { + None => return None, + Some(NodeOrToken::Token(element)) => match element.kind() { + syntax::T![ident] => SmolStr::new(element.text()), + _ => return Some(CfgExpr::Invalid), + }, + Some(_) => return Some(CfgExpr::Invalid), + }; + let result = match name.as_str() { + "all" | "any" | "not" => { + let mut preds = Vec::new(); + let Some(NodeOrToken::Node(tree)) = iter.next() else { + return Some(CfgExpr::Invalid); + }; + let mut tree_iter = tree.token_trees_and_tokens().skip(1).peekable(); + while tree_iter + .peek() + .filter( + |element| matches!(element, NodeOrToken::Token(token) if (token.kind() != syntax::T![')'])), + ) + .is_some() + { + let pred = next_cfg_expr_from_syntax(&mut tree_iter); + if let Some(pred) = pred { + preds.push(pred); + } + } + let group = match name.as_str() { + "all" => CfgExpr::All(preds), + "any" => CfgExpr::Any(preds), + "not" => CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))), + _ => unreachable!(), + }; + Some(group) + } + _ => match iter.peek() { + Some(NodeOrToken::Token(element)) if (element.kind() == syntax::T![=]) => { + iter.next(); + match iter.next() { + Some(NodeOrToken::Token(value_token)) + if (value_token.kind() == syntax::SyntaxKind::STRING) => + { + let value = value_token.text(); + let value = SmolStr::new(value.trim_matches('"')); + Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name, value })) + } + _ => None, + } + } + _ => Some(CfgExpr::Atom(CfgAtom::Flag(name))), + }, + }; + if let Some(NodeOrToken::Token(element)) = iter.peek() { + if element.kind() == syntax::T![,] { + iter.next(); + } + } + result +} +#[cfg(test)] +mod tests { + use cfg::DnfExpr; + use expect_test::{expect, Expect}; + use syntax::{ast::Attr, AstNode, SourceFile}; + + use crate::cfg_process::parse_from_attr_meta; + + fn check_dnf_from_syntax(input: &str, expect: Expect) { + let parse = SourceFile::parse(input); + let node = match parse.tree().syntax().descendants().find_map(Attr::cast) { + Some(it) => it, + None => { + let node = std::any::type_name::(); + panic!("Failed to make ast node `{node}` from text {input}") + } + }; + let node = node.clone_subtree(); + assert_eq!(node.syntax().text_range().start(), 0.into()); + + let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap(); + let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); + expect.assert_eq(&actual); + } + #[test] + fn cfg_from_attr() { + check_dnf_from_syntax(r#"#[cfg(test)]"#, expect![[r#"#![cfg(test)]"#]]); + check_dnf_from_syntax(r#"#[cfg(not(never))]"#, expect![[r#"#![cfg(not(never))]"#]]); + } +} diff --git a/crates/hir-expand/src/change.rs b/crates/hir-expand/src/change.rs index 8b9e5a59df8f..1a3dd0e7ddbd 100644 --- a/crates/hir-expand/src/change.rs +++ b/crates/hir-expand/src/change.rs @@ -48,7 +48,7 @@ impl ChangeWithProcMacros { } } - pub fn change_file(&mut self, file_id: FileId, new_text: Option>) { + pub fn change_file(&mut self, file_id: FileId, new_text: Option) { self.source_change.change_file(file_id, new_text) } diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 6f69ee15acac..ec68f2f96e5e 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -3,21 +3,19 @@ use base_db::{salsa, CrateId, FileId, SourceDatabase}; use either::Either; use limit::Limit; -use mbe::{syntax_node_to_token_tree, ValueResult}; +use mbe::syntax_node_to_token_tree; use rustc_hash::FxHashSet; -use span::{AstIdMap, SyntaxContextData, SyntaxContextId}; -use syntax::{ - ast::{self, HasAttrs}, - AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, -}; +use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId}; +use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T}; use triomphe::Arc; use crate::{ - attrs::collect_attrs, + attrs::{collect_attrs, AttrId}, builtin_attr_macro::pseudo_derive_attr_expansion, builtin_fn_macro::EagerExpander, + cfg_process, declarative::DeclarativeMacroExpander, - fixup::{self, reverse_fixups, SyntaxFixupUndoInfo}, + fixup::{self, SyntaxFixupUndoInfo}, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, proc_macro::ProcMacros, span_map::{RealSpanMap, SpanMap, SpanMapRef}, @@ -100,10 +98,7 @@ pub trait ExpandDatabase: SourceDatabase { /// Lowers syntactic macro call to a token tree representation. That's a firewall /// query, only typing in the macro call itself changes the returned /// subtree. - fn macro_arg( - &self, - id: MacroCallId, - ) -> ValueResult<(Arc, SyntaxFixupUndoInfo), Arc>>; + fn macro_arg(&self, id: MacroCallId) -> (Arc, SyntaxFixupUndoInfo, Span); /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] @@ -120,6 +115,12 @@ pub trait ExpandDatabase: SourceDatabase { /// non-determinism breaks salsa in a very, very, very bad way. /// @edwin0cheng heroically debugged this once! See #4315 for details fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult>; + /// Retrieves the span to be used for a proc-macro expansions spans. + /// This is a firewall query as it requires parsing the file, which we don't want proc-macros to + /// directly depend on as that would cause to frequent invalidations, mainly because of the + /// parse queries being LRU cached. If they weren't the invalidations would only happen if the + /// user wrote in the file that defines the proc-macro. + fn proc_macro_span(&self, fun: AstId) -> Span; /// Firewall query that returns the errors from the `parse_macro_expansion` query. fn parse_macro_expansion_error( &self, @@ -139,30 +140,50 @@ pub fn expand_speculative( ) -> Option<(SyntaxNode, SyntaxToken)> { let loc = db.lookup_intern_macro_call(actual_macro_call); + // FIXME: This BOGUS here is dangerous once the proc-macro server can call back into the database! let span_map = RealSpanMap::absolute(FileId::BOGUS); let span_map = SpanMapRef::RealSpanMap(&span_map); + let (_, _, span) = db.macro_arg(actual_macro_call); + // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { MacroCallKind::FnLike { .. } => ( - mbe::syntax_node_to_token_tree(speculative_args, span_map, loc.call_site), + mbe::syntax_node_to_token_tree(speculative_args, span_map, span), SyntaxFixupUndoInfo::NONE, ), - MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => { - let censor = censor_for_macro_input(&loc, speculative_args); - let mut fixups = fixup::fixup_syntax(span_map, speculative_args, loc.call_site); + MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => ( + mbe::syntax_node_to_token_tree(speculative_args, span_map, span), + SyntaxFixupUndoInfo::NONE, + ), + MacroCallKind::Derive { derive_attr_index: index, .. } + | MacroCallKind::Attr { invoc_attr_index: index, .. } => { + let censor = if let MacroCallKind::Derive { .. } = loc.kind { + censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?) + } else { + attr_source(index, &ast::Item::cast(speculative_args.clone())?) + .into_iter() + .map(|it| it.syntax().clone().into()) + .collect() + }; + + let censor_cfg = + cfg_process::process_cfg_attrs(speculative_args, &loc, db).unwrap_or_default(); + let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span); fixups.append.retain(|it, _| match it { - syntax::NodeOrToken::Node(it) => !censor.contains(it), syntax::NodeOrToken::Token(_) => true, + it => !censor.contains(it) && !censor_cfg.contains(it), }); fixups.remove.extend(censor); + fixups.remove.extend(censor_cfg); + ( mbe::syntax_node_to_token_tree_modified( speculative_args, span_map, fixups.append, fixups.remove, - loc.call_site, + span, ), fixups.undo_info, ) @@ -184,9 +205,8 @@ pub fn expand_speculative( }?; match attr.token_tree() { Some(token_tree) => { - let mut tree = - syntax_node_to_token_tree(token_tree.syntax(), span_map, loc.call_site); - tree.delimiter = tt::Delimiter::invisible_spanned(loc.call_site); + let mut tree = syntax_node_to_token_tree(token_tree.syntax(), span_map, span); + tree.delimiter = tt::Delimiter::invisible_spanned(span); Some(tree) } @@ -199,36 +219,36 @@ pub fn expand_speculative( // Do the actual expansion, we need to directly expand the proc macro due to the attribute args // Otherwise the expand query will fetch the non speculative attribute args and pass those instead. let mut speculative_expansion = match loc.def.kind { - MacroDefKind::ProcMacro(expander, ..) => { - tt.delimiter = tt::Delimiter::invisible_spanned(loc.call_site); + MacroDefKind::ProcMacro(expander, _, ast) => { + let span = db.proc_macro_span(ast); + tt.delimiter = tt::Delimiter::invisible_spanned(span); expander.expand( db, loc.def.krate, loc.krate, &tt, attr_arg.as_ref(), - span_with_def_site_ctxt(db, loc.def.span, actual_macro_call), - span_with_call_site_ctxt(db, loc.def.span, actual_macro_call), - span_with_mixed_site_ctxt(db, loc.def.span, actual_macro_call), + span_with_def_site_ctxt(db, span, actual_macro_call), + span_with_call_site_ctxt(db, span, actual_macro_call), + span_with_mixed_site_ctxt(db, span, actual_macro_call), ) } MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => { - pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, loc.call_site) + pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span) + } + MacroDefKind::Declarative(it) => { + db.decl_macro_expander(loc.krate, it).expand_unhygienic(db, tt, loc.def.krate, span) + } + MacroDefKind::BuiltIn(it, _) => { + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } - MacroDefKind::Declarative(it) => db.decl_macro_expander(loc.krate, it).expand_unhygienic( - db, - tt, - loc.def.krate, - loc.call_site, - ), - MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into), MacroDefKind::BuiltInDerive(it, ..) => { - it.expand(db, actual_macro_call, &tt).map_err(Into::into) + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } MacroDefKind::BuiltInEager(it, _) => { - it.expand(db, actual_macro_call, &tt).map_err(Into::into) + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } - MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt), + MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span), }; let expand_to = loc.expand_to(); @@ -319,181 +339,161 @@ pub(crate) fn parse_with_map( } } -// FIXME: for derive attributes, this will return separate copies of the same structures! +// FIXME: for derive attributes, this will return separate copies of the same structures! Though +// they may differ in spans due to differing call sites... fn macro_arg( db: &dyn ExpandDatabase, id: MacroCallId, - // FIXME: consider the following by putting fixup info into eager call info args - // ) -> ValueResult, Arc>> { -) -> ValueResult<(Arc, SyntaxFixupUndoInfo), Arc>> { +) -> (Arc, SyntaxFixupUndoInfo, Span) { let loc = db.lookup_intern_macro_call(id); - if let Some(EagerCallInfo { arg, .. }) = matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) - .then(|| loc.eager.as_deref()) - .flatten() + + if let MacroCallLoc { + def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. }, + kind: MacroCallKind::FnLike { eager: Some(eager), .. }, + .. + } = &loc { - ValueResult::ok((arg.clone(), SyntaxFixupUndoInfo::NONE)) - } else { - let (parse, map) = parse_with_map(db, loc.kind.file_id()); - let root = parse.syntax_node(); - - let syntax = match loc.kind { - MacroCallKind::FnLike { ast_id, .. } => { - let dummy_tt = |kind| { - ( - Arc::new(tt::Subtree { - delimiter: tt::Delimiter { - open: loc.call_site, - close: loc.call_site, - kind, - }, - token_trees: Box::default(), - }), - SyntaxFixupUndoInfo::default(), - ) - }; - - let node = &ast_id.to_ptr(db).to_node(&root); - let offset = node.syntax().text_range().start(); - let Some(tt) = node.token_tree() else { - return ValueResult::new( - dummy_tt(tt::DelimiterKind::Invisible), - Arc::new(Box::new([SyntaxError::new_at_offset( - "missing token tree".to_owned(), - offset, - )])), - ); - }; - let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']); - let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]); - - let mismatched_delimiters = !matches!( - (first, last), - (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) - ); - if mismatched_delimiters { - // Don't expand malformed (unbalanced) macro invocations. This is - // less than ideal, but trying to expand unbalanced macro calls - // sometimes produces pathological, deeply nested code which breaks - // all kinds of things. - // - // So instead, we'll return an empty subtree here - cov_mark::hit!(issue9358_bad_macro_stack_overflow); - - let kind = match first { - _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible, - T!['('] => tt::DelimiterKind::Parenthesis, - T!['['] => tt::DelimiterKind::Bracket, - T!['{'] => tt::DelimiterKind::Brace, - _ => tt::DelimiterKind::Invisible, - }; - return ValueResult::new( - dummy_tt(kind), - Arc::new(Box::new([SyntaxError::new_at_offset( - "mismatched delimiters".to_owned(), - offset, - )])), - ); - } - tt.syntax().clone() - } - MacroCallKind::Derive { ast_id, .. } => { - ast_id.to_ptr(db).to_node(&root).syntax().clone() - } - MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).to_node(&root).syntax().clone(), - }; - let (mut tt, undo_info) = match loc.kind { - MacroCallKind::FnLike { .. } => ( - mbe::syntax_node_to_token_tree(&syntax, map.as_ref(), loc.call_site), - SyntaxFixupUndoInfo::NONE, - ), - MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => { - let censor = censor_for_macro_input(&loc, &syntax); - let mut fixups = fixup::fixup_syntax(map.as_ref(), &syntax, loc.call_site); - fixups.append.retain(|it, _| match it { - syntax::NodeOrToken::Node(it) => !censor.contains(it), - syntax::NodeOrToken::Token(_) => true, - }); - fixups.remove.extend(censor); - { - let mut tt = mbe::syntax_node_to_token_tree_modified( - &syntax, - map.as_ref(), - fixups.append.clone(), - fixups.remove.clone(), - loc.call_site, - ); - reverse_fixups(&mut tt, &fixups.undo_info); - } - ( - mbe::syntax_node_to_token_tree_modified( - &syntax, - map, - fixups.append, - fixups.remove, - loc.call_site, - ), - fixups.undo_info, - ) - } - }; - - if loc.def.is_proc_macro() { - // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.delimiter.kind = tt::DelimiterKind::Invisible; - } - - if matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) { - match parse.errors() { - errors if errors.is_empty() => ValueResult::ok((Arc::new(tt), undo_info)), - errors => ValueResult::new( - (Arc::new(tt), undo_info), - // Box::<[_]>::from(res.errors()), not stable yet - Arc::new(errors.to_vec().into_boxed_slice()), - ), - } - } else { - ValueResult::ok((Arc::new(tt), undo_info)) - } + return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span); } + + let (parse, map) = parse_with_map(db, loc.kind.file_id()); + let root = parse.syntax_node(); + + let (censor, item_node, span) = match loc.kind { + MacroCallKind::FnLike { ast_id, .. } => { + let node = &ast_id.to_ptr(db).to_node(&root); + let path_range = node + .path() + .map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range()); + let span = map.span_for_range(path_range); + + let dummy_tt = |kind| { + ( + Arc::new(tt::Subtree { + delimiter: tt::Delimiter { open: span, close: span, kind }, + token_trees: Box::default(), + }), + SyntaxFixupUndoInfo::default(), + span, + ) + }; + + let Some(tt) = node.token_tree() else { + return dummy_tt(tt::DelimiterKind::Invisible); + }; + let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']); + let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]); + + let mismatched_delimiters = !matches!( + (first, last), + (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) + ); + if mismatched_delimiters { + // Don't expand malformed (unbalanced) macro invocations. This is + // less than ideal, but trying to expand unbalanced macro calls + // sometimes produces pathological, deeply nested code which breaks + // all kinds of things. + // + // So instead, we'll return an empty subtree here + cov_mark::hit!(issue9358_bad_macro_stack_overflow); + + let kind = match first { + _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible, + T!['('] => tt::DelimiterKind::Parenthesis, + T!['['] => tt::DelimiterKind::Bracket, + T!['{'] => tt::DelimiterKind::Brace, + _ => tt::DelimiterKind::Invisible, + }; + return dummy_tt(kind); + } + + let mut tt = mbe::syntax_node_to_token_tree(tt.syntax(), map.as_ref(), span); + if loc.def.is_proc_macro() { + // proc macros expect their inputs without parentheses, MBEs expect it with them included + tt.delimiter.kind = tt::DelimiterKind::Invisible; + } + return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); + } + MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { + let node = ast_id.to_ptr(db).to_node(&root); + let censor_derive_input = censor_derive_input(derive_attr_index, &node); + let item_node = node.into(); + let attr_source = attr_source(derive_attr_index, &item_node); + // FIXME: This is wrong, this should point to the path of the derive attribute` + let span = + map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else( + || item_node.syntax().text_range(), + |it| it.syntax().text_range(), + )); + (censor_derive_input, item_node, span) + } + MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { + let node = ast_id.to_ptr(db).to_node(&root); + let attr_source = attr_source(invoc_attr_index, &node); + let span = map.span_for_range( + attr_source + .as_ref() + .and_then(|it| it.path()) + .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()), + ); + (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) + } + }; + + let (mut tt, undo_info) = { + let syntax = item_node.syntax(); + let censor_cfg = cfg_process::process_cfg_attrs(syntax, &loc, db).unwrap_or_default(); + let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span); + fixups.append.retain(|it, _| match it { + syntax::NodeOrToken::Token(_) => true, + it => !censor.contains(it) && !censor_cfg.contains(it), + }); + fixups.remove.extend(censor); + fixups.remove.extend(censor_cfg); + + ( + mbe::syntax_node_to_token_tree_modified( + syntax, + map, + fixups.append, + fixups.remove, + span, + ), + fixups.undo_info, + ) + }; + + if loc.def.is_proc_macro() { + // proc macros expect their inputs without parentheses, MBEs expect it with them included + tt.delimiter.kind = tt::DelimiterKind::Invisible; + } + + (Arc::new(tt), undo_info, span) } // FIXME: Censoring info should be calculated by the caller! Namely by name resolution -/// Certain macro calls expect some nodes in the input to be preprocessed away, namely: -/// - derives expect all `#[derive(..)]` invocations up to the currently invoked one to be stripped -/// - attributes expect the invoking attribute to be stripped -fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet { +/// Derives expect all `#[derive(..)]` invocations up to (and including) the currently invoked one to be stripped +fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet { // FIXME: handle `cfg_attr` - (|| { - let censor = match loc.kind { - MacroCallKind::FnLike { .. } => return None, - MacroCallKind::Derive { derive_attr_index, .. } => { - cov_mark::hit!(derive_censoring); - ast::Item::cast(node.clone())? - .attrs() - .take(derive_attr_index.ast_index() + 1) - // FIXME, this resolution should not be done syntactically - // derive is a proper macro now, no longer builtin - // But we do not have resolution at this stage, this means - // we need to know about all macro calls for the given ast item here - // so we require some kind of mapping... - .filter(|attr| attr.simple_name().as_deref() == Some("derive")) - .map(|it| it.syntax().clone()) - .collect() - } - MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None, - MacroCallKind::Attr { invoc_attr_index, .. } => { - cov_mark::hit!(attribute_macro_attr_censoring); - collect_attrs(&ast::Item::cast(node.clone())?) - .nth(invoc_attr_index.ast_index()) - .and_then(|x| Either::left(x.1)) - .map(|attr| attr.syntax().clone()) - .into_iter() - .collect() - } - }; - Some(censor) - })() - .unwrap_or_default() + cov_mark::hit!(derive_censoring); + collect_attrs(node) + .take(derive_attr_index.ast_index() + 1) + .filter_map(|(_, attr)| Either::left(attr)) + // FIXME, this resolution should not be done syntactically + // derive is a proper macro now, no longer builtin + // But we do not have resolution at this stage, this means + // we need to know about all macro calls for the given ast item here + // so we require some kind of mapping... + .filter(|attr| attr.simple_name().as_deref() == Some("derive")) + .map(|it| it.syntax().clone().into()) + .collect() +} + +/// Attributes expect the invoking attribute to be stripped +fn attr_source(invoc_attr_index: AttrId, node: &ast::Item) -> Option { + // FIXME: handle `cfg_attr` + cov_mark::hit!(attribute_macro_attr_censoring); + collect_attrs(node).nth(invoc_attr_index.ast_index()).and_then(|(_, attr)| Either::left(attr)) } impl TokenExpander { @@ -523,74 +523,64 @@ fn macro_expand( ) -> ExpandResult> { let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered(); - let ExpandResult { value: tt, mut err } = match loc.def.kind { + let (ExpandResult { value: tt, err }, span) = match loc.def.kind { MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc), _ => { - let ValueResult { value: (macro_arg, undo_info), err } = db.macro_arg(macro_call_id); - let format_parse_err = |err: Arc>| { - let mut buf = String::new(); - for err in &**err { - use std::fmt::Write; - _ = write!(buf, "{}, ", err); - } - buf.pop(); - buf.pop(); - ExpandError::other(buf) - }; + let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id); let arg = &*macro_arg; - let res = match loc.def.kind { - MacroDefKind::Declarative(id) => { - db.decl_macro_expander(loc.def.krate, id).expand(db, arg.clone(), macro_call_id) - } - MacroDefKind::BuiltIn(it, _) => { - it.expand(db, macro_call_id, arg).map_err(Into::into) - } - // This might look a bit odd, but we do not expand the inputs to eager macros here. - // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. - // That kind of expansion uses the ast id map of an eager macros input though which goes through - // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query - // will end up going through here again, whereas we want to just want to inspect the raw input. - // As such we just return the input subtree here. - MacroDefKind::BuiltInEager(..) if loc.eager.is_none() => { - return ExpandResult { - value: CowArc::Arc(macro_arg.clone()), - err: err.map(format_parse_err), - }; - } - MacroDefKind::BuiltInDerive(it, _) => { - it.expand(db, macro_call_id, arg).map_err(Into::into) - } - MacroDefKind::BuiltInEager(it, _) => { - it.expand(db, macro_call_id, arg).map_err(Into::into) - } - MacroDefKind::BuiltInAttr(it, _) => { - let mut res = it.expand(db, macro_call_id, arg); - fixup::reverse_fixups(&mut res.value, &undo_info); - res - } - _ => unreachable!(), - }; - ExpandResult { - value: res.value, - // if the arg had parse errors, show them instead of the expansion errors - err: err.map(format_parse_err).or(res.err), - } + let res = + match loc.def.kind { + MacroDefKind::Declarative(id) => db + .decl_macro_expander(loc.def.krate, id) + .expand(db, arg.clone(), macro_call_id, span), + MacroDefKind::BuiltIn(it, _) => { + it.expand(db, macro_call_id, arg, span).map_err(Into::into) + } + MacroDefKind::BuiltInDerive(it, _) => { + it.expand(db, macro_call_id, arg, span).map_err(Into::into) + } + MacroDefKind::BuiltInEager(it, _) => { + // This might look a bit odd, but we do not expand the inputs to eager macros here. + // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. + // That kind of expansion uses the ast id map of an eager macros input though which goes through + // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query + // will end up going through here again, whereas we want to just want to inspect the raw input. + // As such we just return the input subtree here. + let eager = match &loc.kind { + MacroCallKind::FnLike { eager: None, .. } => { + return ExpandResult::ok(CowArc::Arc(macro_arg.clone())); + } + MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager), + _ => None, + }; + + let mut res = it.expand(db, macro_call_id, arg, span).map_err(Into::into); + + if let Some(EagerCallInfo { error, .. }) = eager { + // FIXME: We should report both errors! + res.err = error.clone().or(res.err); + } + res + } + MacroDefKind::BuiltInAttr(it, _) => { + let mut res = it.expand(db, macro_call_id, arg, span); + fixup::reverse_fixups(&mut res.value, &undo_info); + res + } + _ => unreachable!(), + }; + (ExpandResult { value: res.value, err: res.err }, span) } }; - if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() { - // FIXME: We should report both errors! - err = error.clone().or(err); - } - // Skip checking token tree limit for include! macro call if !loc.def.is_include() { // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value.map(|()| { CowArc::Owned(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(loc.call_site), + delimiter: tt::Delimiter::invisible_spanned(span), token_trees: Box::new([]), }) }); @@ -600,12 +590,23 @@ fn macro_expand( ExpandResult { value: CowArc::Owned(tt), err } } +fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { + let root = db.parse_or_expand(ast.file_id); + let ast_id_map = &db.ast_id_map(ast.file_id); + let span_map = &db.span_map(ast.file_id); + + let node = ast_id_map.get(ast.value).to_node(&root); + let range = ast::HasName::name(&node) + .map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range()); + span_map.span_for_range(range) +} + fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult> { let loc = db.lookup_intern_macro_call(id); - let (macro_arg, undo_info) = db.macro_arg(id).value; + let (macro_arg, undo_info, span) = db.macro_arg(id); - let expander = match loc.def.kind { - MacroDefKind::ProcMacro(expander, ..) => expander, + let (expander, ast) = match loc.def.kind { + MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast), _ => unreachable!(), }; @@ -614,22 +615,25 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult None, }; - let ExpandResult { value: mut tt, err } = expander.expand( - db, - loc.def.krate, - loc.krate, - ¯o_arg, - attr_arg, - span_with_def_site_ctxt(db, loc.def.span, id), - span_with_call_site_ctxt(db, loc.def.span, id), - span_with_mixed_site_ctxt(db, loc.def.span, id), - ); + let ExpandResult { value: mut tt, err } = { + let span = db.proc_macro_span(ast); + expander.expand( + db, + loc.def.krate, + loc.krate, + ¯o_arg, + attr_arg, + span_with_def_site_ctxt(db, span, id), + span_with_call_site_ctxt(db, span, id), + span_with_mixed_site_ctxt(db, span, id), + ) + }; // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value.map(|()| { Arc::new(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(loc.call_site), + delimiter: tt::Delimiter::invisible_spanned(span), token_trees: Box::new([]), }) }); diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index 6874336cd2d0..33643c02724f 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/crates/hir-expand/src/declarative.rs @@ -29,6 +29,7 @@ impl DeclarativeMacroExpander { db: &dyn ExpandDatabase, tt: tt::Subtree, call_id: MacroCallId, + span: Span, ) -> ExpandResult { let loc = db.lookup_intern_macro_call(call_id); let toolchain = db.toolchain(loc.def.krate); @@ -45,7 +46,7 @@ impl DeclarativeMacroExpander { }); match self.mac.err() { Some(_) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }), + tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), ExpandError::MacroDefinition, ), None => self @@ -54,7 +55,7 @@ impl DeclarativeMacroExpander { &tt, |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), new_meta_vars, - loc.call_site, + span, ) .map_err(Into::into), } diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index 5337a5bb028c..8b147c88c13c 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs @@ -19,7 +19,7 @@ //! //! See the full discussion : use base_db::CrateId; -use span::Span; +use span::SyntaxContextId; use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent}; use triomphe::Arc; @@ -27,22 +27,20 @@ use crate::{ ast::{self, AstNode}, db::ExpandDatabase, mod_path::ModPath, - EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern, + AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, }; pub fn expand_eager_macro_input( db: &dyn ExpandDatabase, krate: CrateId, - macro_call: InFile, + macro_call: &ast::MacroCall, + ast_id: AstId, def: MacroDefId, - call_site: Span, + call_site: SyntaxContextId, resolver: &dyn Fn(ModPath) -> Option, ) -> ExpandResult> { - let ast_map = db.ast_id_map(macro_call.file_id); - // the expansion which the ast id map is built upon has no whitespace, so the offsets are wrong as macro_call is from the token tree that has whitespace! - let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value)); - let expand_to = ExpandTo::from_call_site(¯o_call.value); + let expand_to = ExpandTo::from_call_site(macro_call); // Note: // When `lazy_expand` is called, its *parent* file must already exist. @@ -51,11 +49,11 @@ pub fn expand_eager_macro_input( let arg_id = MacroCallLoc { def, krate, - eager: None, - kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr }, - call_site, + kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None }, + ctxt: call_site, } .intern(db); + let (_, _, span) = db.macro_arg(arg_id); let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } = db.parse_macro_expansion(arg_id.as_macro_file()); @@ -82,16 +80,24 @@ pub fn expand_eager_macro_input( return ExpandResult { value: None, err }; }; - let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, call_site); + let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, span); subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible; let loc = MacroCallLoc { def, krate, - eager: Some(Arc::new(EagerCallInfo { arg: Arc::new(subtree), arg_id, error: err.clone() })), - kind: MacroCallKind::FnLike { ast_id: call_id, expand_to }, - call_site, + kind: MacroCallKind::FnLike { + ast_id, + expand_to, + eager: Some(Arc::new(EagerCallInfo { + arg: Arc::new(subtree), + arg_id, + error: err.clone(), + span, + })), + }, + ctxt: call_site, }; ExpandResult { value: Some(loc.intern(db)), err } @@ -100,15 +106,18 @@ pub fn expand_eager_macro_input( fn lazy_expand( db: &dyn ExpandDatabase, def: &MacroDefId, - macro_call: InFile, + macro_call: &ast::MacroCall, + ast_id: AstId, krate: CrateId, - call_site: Span, + call_site: SyntaxContextId, ) -> ExpandResult<(InFile>, Arc)> { - let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); - - let expand_to = ExpandTo::from_call_site(¯o_call.value); - let ast_id = macro_call.with_value(ast_id); - let id = def.as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id, expand_to }, call_site); + let expand_to = ExpandTo::from_call_site(macro_call); + let id = def.make_call( + db, + krate, + MacroCallKind::FnLike { ast_id, expand_to, eager: None }, + call_site, + ); let macro_file = id.as_macro_file(); db.parse_macro_expansion(macro_file) @@ -122,7 +131,7 @@ fn eager_macro_recur( mut offset: TextSize, curr: InFile, krate: CrateId, - call_site: Span, + call_site: SyntaxContextId, macro_resolver: &dyn Fn(ModPath) -> Option, ) -> ExpandResult> { let original = curr.value.clone_for_update(); @@ -172,12 +181,14 @@ fn eager_macro_recur( continue; } }; + let ast_id = db.ast_id_map(curr.file_id).ast_id(&call); let ExpandResult { value, err } = match def.kind { MacroDefKind::BuiltInEager(..) => { let ExpandResult { value, err } = expand_eager_macro_input( db, krate, - curr.with_value(call.clone()), + &call, + curr.with_value(ast_id), def, call_site, macro_resolver, @@ -207,7 +218,7 @@ fn eager_macro_recur( | MacroDefKind::BuiltInDerive(..) | MacroDefKind::ProcMacro(..) => { let ExpandResult { value: (parse, tm), err } = - lazy_expand(db, &def, curr.with_value(call.clone()), krate, call_site); + lazy_expand(db, &def, &call, curr.with_value(ast_id), krate, call_site); // replace macro inside let ExpandResult { value, err: error } = eager_macro_recur( diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index a500c24ce881..04a4851ddb72 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -10,7 +10,7 @@ use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, use crate::{ db::{self, ExpandDatabase}, - map_node_range_up, span_for_offset, MacroFileIdExt, + map_node_range_up, map_node_range_up_rooted, span_for_offset, MacroFileIdExt, }; /// `InFile` stores a value of `T` inside a particular file/syntax tree. @@ -38,6 +38,9 @@ impl AstId { pub fn to_node(&self, db: &dyn ExpandDatabase) -> N { self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) } + pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange { + self.to_ptr(db).text_range() + } pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile { crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))) } @@ -49,6 +52,9 @@ impl AstId { pub type ErasedAstId = crate::InFile; impl ErasedAstId { + pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange { + self.to_ptr(db).text_range() + } pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr { db.ast_id_map(self.file_id).get_erased(self.value) } @@ -173,24 +179,8 @@ impl InFile<&SyntaxNode> { /// /// For attributes and derives, this will point back to the attribute only. /// For the entire item use [`InFile::original_file_range_full`]. - pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange { - match self.file_id.repr() { - HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, - HirFileIdRepr::MacroFile(mac_file) => { - if let Some((res, ctxt)) = - map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range()) - { - // FIXME: Figure out an API that makes proper use of ctx, this only exists to - // keep pre-token map rewrite behaviour. - if ctxt.is_root() { - return res; - } - } - // Fall back to whole macro call. - let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); - loc.kind.original_call_range(db) - } - } + pub fn original_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange { + self.map(SyntaxNode::text_range).original_node_file_range_rooted(db) } /// Falls back to the macro call range if the node cannot be mapped up fully. @@ -198,23 +188,7 @@ impl InFile<&SyntaxNode> { self, db: &dyn db::ExpandDatabase, ) -> FileRange { - match self.file_id.repr() { - HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, - HirFileIdRepr::MacroFile(mac_file) => { - if let Some((res, ctxt)) = - map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range()) - { - // FIXME: Figure out an API that makes proper use of ctx, this only exists to - // keep pre-token map rewrite behaviour. - if ctxt.is_root() { - return res; - } - } - // Fall back to whole macro call. - let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); - loc.kind.original_call_range_with_body(db) - } - } + self.map(SyntaxNode::text_range).original_node_file_range_with_macro_call_body(db) } /// Attempts to map the syntax node back up its macro calls. @@ -222,17 +196,10 @@ impl InFile<&SyntaxNode> { self, db: &dyn db::ExpandDatabase, ) -> Option<(FileRange, SyntaxContextId)> { - match self.file_id.repr() { - HirFileIdRepr::FileId(file_id) => { - Some((FileRange { file_id, range: self.value.text_range() }, SyntaxContextId::ROOT)) - } - HirFileIdRepr::MacroFile(mac_file) => { - map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range()) - } - } + self.map(SyntaxNode::text_range).original_node_file_range_opt(db) } - pub fn original_syntax_node( + pub fn original_syntax_node_rooted( self, db: &dyn db::ExpandDatabase, ) -> Option> { @@ -242,25 +209,21 @@ impl InFile<&SyntaxNode> { HirFileIdRepr::FileId(file_id) => { return Some(InRealFile { file_id, value: self.value.clone() }) } - HirFileIdRepr::MacroFile(m) => m, + HirFileIdRepr::MacroFile(m) if m.is_attr_macro(db) => m, + _ => return None, }; - if !file_id.is_attr_macro(db) { - return None; - } - let (FileRange { file_id, range }, ctx) = - map_node_range_up(db, &db.expansion_span_map(file_id), self.value.text_range())?; + let FileRange { file_id, range } = + map_node_range_up_rooted(db, &db.expansion_span_map(file_id), self.value.text_range())?; - // FIXME: Figure out an API that makes proper use of ctx, this only exists to - // keep pre-token map rewrite behavior. - if !ctx.is_root() { - return None; - } - - let anc = db.parse(file_id).syntax_node().covering_element(range); let kind = self.value.kind(); - // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes? - let value = anc.ancestors().find(|it| it.kind() == kind)?; + let value = db + .parse(file_id) + .syntax_node() + .covering_element(range) + .ancestors() + .take_while(|it| it.text_range() == range) + .find(|it| it.kind() == kind)?; Some(InRealFile::new(file_id, value)) } } @@ -355,8 +318,8 @@ impl InFile { match self.file_id.repr() { HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value }, HirFileIdRepr::MacroFile(mac_file) => { - match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) { - Some((it, SyntaxContextId::ROOT)) => it, + match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) { + Some(it) => it, _ => { let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); loc.kind.original_call_range(db) @@ -366,6 +329,24 @@ impl InFile { } } + pub fn original_node_file_range_with_macro_call_body( + self, + db: &dyn db::ExpandDatabase, + ) -> FileRange { + match self.file_id.repr() { + HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value }, + HirFileIdRepr::MacroFile(mac_file) => { + match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) { + Some(it) => it, + _ => { + let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); + loc.kind.original_call_range_with_body(db) + } + } + } + } + } + pub fn original_node_file_range_opt( self, db: &dyn db::ExpandDatabase, @@ -395,18 +376,12 @@ impl InFile { return None; } - let (FileRange { file_id, range }, ctx) = map_node_range_up( + let FileRange { file_id, range } = map_node_range_up_rooted( db, &db.expansion_span_map(file_id), self.value.syntax().text_range(), )?; - // FIXME: Figure out an API that makes proper use of ctx, this only exists to - // keep pre-token map rewrite behaviour. - if !ctx.is_root() { - return None; - } - // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes? let anc = db.parse(file_id).syntax_node().covering_element(range); let value = anc.ancestors().find_map(N::cast)?; diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index a3b2c700ffea..959595afb572 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -3,7 +3,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; -use span::{ErasedFileAstId, Span, SpanAnchor, SpanData, FIXUP_ERASED_FILE_AST_ID_MARKER}; +use span::{ErasedFileAstId, Span, SpanAnchor, FIXUP_ERASED_FILE_AST_ID_MARKER}; use stdx::never; use syntax::{ ast::{self, AstNode, HasLoopBody}, @@ -23,7 +23,7 @@ use crate::{ #[derive(Debug, Default)] pub(crate) struct SyntaxFixups { pub(crate) append: FxHashMap>, - pub(crate) remove: FxHashSet, + pub(crate) remove: FxHashSet, pub(crate) undo_info: SyntaxFixupUndoInfo, } @@ -51,13 +51,13 @@ pub(crate) fn fixup_syntax( call_site: Span, ) -> SyntaxFixups { let mut append = FxHashMap::::default(); - let mut remove = FxHashSet::::default(); + let mut remove = FxHashSet::::default(); let mut preorder = node.preorder(); let mut original = Vec::new(); let dummy_range = FIXUP_DUMMY_RANGE; let fake_span = |range| { let span = span_map.span_for_range(range); - SpanData { + Span { range: dummy_range, anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor }, ctx: span.ctx, @@ -68,7 +68,7 @@ pub(crate) fn fixup_syntax( let node_range = node.text_range(); if can_handle_error(&node) && has_error_to_handle(&node) { - remove.insert(node.clone()); + remove.insert(node.clone().into()); // the node contains an error node, we have to completely replace it by something valid let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site); let idx = original.len() as u32; @@ -76,7 +76,7 @@ pub(crate) fn fixup_syntax( let span = span_map.span_for_range(node_range); let replacement = Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: SpanData { + span: Span { range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END), anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor }, ctx: span.ctx, @@ -305,8 +305,8 @@ pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID ) { - tt.delimiter.close = SpanData::DUMMY; - tt.delimiter.open = SpanData::DUMMY; + tt.delimiter.close = Span::DUMMY; + tt.delimiter.open = Span::DUMMY; } reverse_fixups_(tt, undo_info); } diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index ac2bab280d50..097e760c70ab 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -65,7 +65,7 @@ pub(super) fn apply_mark( return apply_mark_internal(db, ctxt, call_id, transparency); } - let call_site_ctxt = db.lookup_intern_macro_call(call_id).call_site.ctx; + let call_site_ctxt = db.lookup_intern_macro_call(call_id).ctxt; let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { call_site_ctxt.normalize_to_macros_2_0(db) } else { @@ -205,11 +205,10 @@ pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String { let id = e.key; let expn_data = e.value.as_ref().unwrap(); s.push_str(&format!( - "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", + "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, kind: {:?}", id, expn_data.kind.file_id(), - expn_data.call_site, - SyntaxContextId::ROOT, // FIXME expn_data.def_site, + expn_data.ctxt, expn_data.kind.descr(), )); } diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 42dc8c12d60b..5d4f7dc1462a 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -22,16 +22,19 @@ pub mod proc_macro; pub mod quote; pub mod span_map; +mod cfg_process; mod fixup; - use attrs::collect_attrs; +use rustc_hash::FxHashMap; use triomphe::Arc; use std::{fmt, hash::Hash}; use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId}; use either::Either; -use span::{ErasedFileAstId, FileRange, HirFileIdRepr, Span, SyntaxContextData, SyntaxContextId}; +use span::{ + ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId, +}; use syntax::{ ast::{self, AstNode}, SyntaxNode, SyntaxToken, TextRange, TextSize, @@ -167,13 +170,8 @@ impl fmt::Display for ExpandError { pub struct MacroCallLoc { pub def: MacroDefId, pub krate: CrateId, - /// Some if this is a macro call for an eager macro. Note that this is `None` - /// for the eager input macro file. - // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing - // leakage problems here - eager: Option>, pub kind: MacroCallKind, - pub call_site: Span, + pub ctxt: SyntaxContextId, } impl_intern_value_trivial!(MacroCallLoc); @@ -184,7 +182,6 @@ pub struct MacroDefId { pub kind: MacroDefKind, pub local_inner: bool, pub allow_internal_unsafe: bool, - pub span: Span, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -204,6 +201,8 @@ pub struct EagerCallInfo { /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option, + /// TODO: Doc + span: Span, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -211,6 +210,11 @@ pub enum MacroCallKind { FnLike { ast_id: AstId, expand_to: ExpandTo, + /// Some if this is a macro call for an eager macro. Note that this is `None` + /// for the eager input macro file. + // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing + // leakage problems here + eager: Option>, }, Derive { ast_id: AstId, @@ -272,7 +276,7 @@ impl HirFileIdExt for HirFileId { HirFileIdRepr::MacroFile(file) => { let loc = db.lookup_intern_macro_call(file.macro_call_id); if loc.def.is_include() { - if let Some(eager) = &loc.eager { + if let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind { if let Ok(it) = builtin_fn_macro::include_input_to_file_id( db, file.macro_call_id, @@ -319,6 +323,9 @@ impl HirFileIdExt for HirFileId { } pub trait MacroFileIdExt { + fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool; + fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool; + fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option; fn expansion_level(self, db: &dyn ExpandDatabase) -> u32; /// If this is a macro call, returns the syntax node of the call. fn call_node(self, db: &dyn ExpandDatabase) -> InFile; @@ -385,31 +392,47 @@ impl MacroFileIdExt for MacroFileId { db.lookup_intern_macro_call(self.macro_call_id).def.is_include() } + fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool { + db.lookup_intern_macro_call(self.macro_call_id).def.is_include_like() + } + + fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool { + db.lookup_intern_macro_call(self.macro_call_id).def.is_env_or_option_env() + } + fn is_eager(&self, db: &dyn ExpandDatabase) -> bool { - let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + let loc = db.lookup_intern_macro_call(self.macro_call_id); matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) } + fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option { + let loc = db.lookup_intern_macro_call(self.macro_call_id); + match &loc.kind { + MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id), + _ => None, + } + } + fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool { - let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + let loc = db.lookup_intern_macro_call(self.macro_call_id); matches!(loc.kind, MacroCallKind::Attr { .. }) } fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool { - let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + let loc = db.lookup_intern_macro_call(self.macro_call_id); loc.def.is_attribute_derive() } } impl MacroDefId { - pub fn as_lazy_macro( + pub fn make_call( self, db: &dyn ExpandDatabase, krate: CrateId, kind: MacroCallKind, - call_site: Span, + ctxt: SyntaxContextId, ) -> MacroCallId { - MacroCallLoc { def: self, krate, eager: None, kind, call_site }.intern(db) + MacroCallLoc { def: self, krate, kind, ctxt }.intern(db) } pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile { @@ -474,6 +497,14 @@ impl MacroDefId { pub fn is_include(&self) -> bool { matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include()) } + + pub fn is_include_like(&self) -> bool { + matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include_like()) + } + + pub fn is_env_or_option_env(&self) -> bool { + matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_env_or_option_env()) + } } impl MacroCallLoc { @@ -531,7 +562,7 @@ impl MacroCallLoc { macro_call_id: MacroCallId, ) -> Option { if self.def.is_include() { - if let Some(eager) = &self.eager { + if let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind { if let Ok(it) = builtin_fn_macro::include_input_to_file_id(db, macro_call_id, &eager.arg) { @@ -655,7 +686,7 @@ impl MacroCallKind { /// ExpansionInfo mainly describes how to map text range between src and expanded macro // FIXME: can be expensive to create, we should check the use sites and maybe replace them with // simpler function calls if the map is only used once -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct ExpansionInfo { pub expanded: InMacroFile, /// The argument TokenTree or item for attributes @@ -683,6 +714,24 @@ impl ExpansionInfo { } /// Maps the passed in file range down into a macro expansion if it is the input to a macro call. + /// + /// Note this does a linear search through the entire backing vector of the spanmap. + pub fn map_range_down_exact( + &self, + span: Span, + ) -> Option + '_>> { + let tokens = self + .exp_map + .ranges_with_span_exact(span) + .flat_map(move |range| self.expanded.value.covering_element(range).into_token()); + + Some(InMacroFile::new(self.expanded.file_id, tokens)) + } + + /// Maps the passed in file range down into a macro expansion if it is the input to a macro call. + /// Unlike [`map_range_down_exact`], this will consider spans that contain the given span. + /// + /// Note this does a linear search through the entire backing vector of the spanmap. pub fn map_range_down( &self, span: Span, @@ -739,7 +788,7 @@ impl ExpansionInfo { InFile::new( self.arg.file_id, arg_map - .ranges_with_span(span) + .ranges_with_span_exact(span) .filter(|range| range.intersect(arg_range).is_some()) .collect(), ) @@ -757,7 +806,7 @@ impl ExpansionInfo { let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; - let (macro_arg, _) = db.macro_arg(macro_file.macro_call_id).value; + let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id); let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { @@ -793,7 +842,34 @@ impl ExpansionInfo { } } +/// Maps up the text range out of the expansion hierarchy back into the original file its from only +/// considering the root spans contained. +/// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ. +pub fn map_node_range_up_rooted( + db: &dyn ExpandDatabase, + exp_map: &ExpansionSpanMap, + range: TextRange, +) -> Option { + let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root()); + let Span { range, anchor, ctx: _ } = spans.next()?; + let mut start = range.start(); + let mut end = range.end(); + + for span in spans { + if span.anchor != anchor { + return None; + } + start = start.min(span.range.start()); + end = end.max(span.range.end()); + } + let anchor_offset = + db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start(); + Some(FileRange { file_id: anchor.file_id, range: TextRange::new(start, end) + anchor_offset }) +} + /// Maps up the text range out of the expansion hierarchy back into the original file its from. +/// +/// this will return `None` if any anchors or syntax contexts differ. pub fn map_node_range_up( db: &dyn ExpandDatabase, exp_map: &ExpansionSpanMap, @@ -819,6 +895,29 @@ pub fn map_node_range_up( )) } +/// Maps up the text range out of the expansion hierarchy back into the original file its from. +/// This version will aggregate the ranges of all spans with the same anchor and syntax context. +pub fn map_node_range_up_aggregated( + db: &dyn ExpandDatabase, + exp_map: &ExpansionSpanMap, + range: TextRange, +) -> FxHashMap<(SpanAnchor, SyntaxContextId), TextRange> { + let mut map = FxHashMap::default(); + for span in exp_map.spans_for_range(range) { + let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range); + *range = TextRange::new( + range.start().min(span.range.start()), + range.end().max(span.range.end()), + ); + } + for ((anchor, _), range) in &mut map { + let anchor_offset = + db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start(); + *range += anchor_offset; + } + map +} + /// Looks up the span at the given offset. pub fn span_for_offset( db: &dyn ExpandDatabase, diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs index c1930c94f5c9..a31a111c9117 100644 --- a/crates/hir-expand/src/quote.rs +++ b/crates/hir-expand/src/quote.rs @@ -266,10 +266,11 @@ mod tests { let quoted = quote!(DUMMY =>#a); assert_eq!(quoted.to_string(), "hello"); - let t = format!("{quoted:?}"); + let t = format!("{quoted:#?}"); expect![[r#" - SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } - IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t); + SUBTREE $$ 937550:0@0..0#0 937550:0@0..0#0 + IDENT hello 937550:0@0..0#0"#]] + .assert_eq(&t); } #[test] diff --git a/crates/hir-expand/src/span_map.rs b/crates/hir-expand/src/span_map.rs index ef86be67096a..eae2c8fb632b 100644 --- a/crates/hir-expand/src/span_map.rs +++ b/crates/hir-expand/src/span_map.rs @@ -1,13 +1,15 @@ //! Span maps for real files and macro expansions. -use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span}; -use syntax::{AstNode, TextRange}; + +use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span, SyntaxContextId}; +use stdx::TupleExt; +use syntax::{ast, AstNode, TextRange}; use triomphe::Arc; pub use span::RealSpanMap; -use crate::db::ExpandDatabase; +use crate::{attrs::collect_attrs, db::ExpandDatabase}; -pub type ExpansionSpanMap = span::SpanMap; +pub type ExpansionSpanMap = span::SpanMap; /// Spanmap for a macro file or a real file #[derive(Clone, Debug, PartialEq, Eq)] @@ -82,13 +84,54 @@ pub(crate) fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc + { + if let Some(extern_item_list) = it.extern_item_list() { + pairs.extend( + extern_item_list.extern_items().map(ast::Item::from).map(item_to_entry), + ); + } + } + ast::Item::Impl(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + if let Some(assoc_item_list) = it.assoc_item_list() { + pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry)); + } + } + ast::Item::Module(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + if let Some(item_list) = it.item_list() { + pairs.extend(item_list.items().map(item_to_entry)); + } + } + ast::Item::Trait(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + if let Some(assoc_item_list) = it.assoc_item_list() { + pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry)); + } + } + _ => (), + }); Arc::new(RealSpanMap::from_file( file_id, diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 41e2f7ad73c3..3cfedcdcb4dc 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -23,10 +23,10 @@ oorandom = "11.1.3" tracing.workspace = true rustc-hash.workspace = true scoped-tls = "1.0.0" -chalk-solve = { version = "0.96.0", default-features = false } -chalk-ir = "0.96.0" -chalk-recursive = { version = "0.96.0", default-features = false } -chalk-derive = "0.96.0" +chalk-solve.workspace = true +chalk-ir.workspace = true +chalk-recursive.workspace = true +chalk-derive.workspace = true la-arena.workspace = true once_cell = "1.17.0" triomphe.workspace = true diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs index 8d819e41aa2c..e2446c34254a 100644 --- a/crates/hir-ty/src/autoderef.rs +++ b/crates/hir-ty/src/autoderef.rs @@ -113,7 +113,7 @@ pub(crate) fn autoderef_step( ty: Ty, explicit: bool, ) -> Option<(AutoderefKind, Ty)> { - if let Some(derefed) = builtin_deref(table, &ty, explicit) { + if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) @@ -121,7 +121,7 @@ pub(crate) fn autoderef_step( } pub(crate) fn builtin_deref<'ty>( - table: &mut InferenceTable<'_>, + db: &dyn HirDatabase, ty: &'ty Ty, explicit: bool, ) -> Option<&'ty Ty> { @@ -129,7 +129,7 @@ pub(crate) fn builtin_deref<'ty>( TyKind::Ref(.., ty) => Some(ty), TyKind::Raw(.., ty) if explicit => Some(ty), &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => { - if crate::lang_items::is_box(table.db, adt) { + if crate::lang_items::is_box(db, adt) { substs.at(Interner, 0).ty(Interner) } else { None diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 9cea414e1a00..34ba17f145e0 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -22,7 +22,7 @@ mod pat; mod path; pub(crate) mod unify; -use std::{convert::identity, ops::Index}; +use std::{convert::identity, iter, ops::Index}; use chalk_ir::{ cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, @@ -777,7 +777,15 @@ impl<'a> InferenceContext<'a> { param_tys.push(va_list_ty) } - for (ty, pat) in param_tys.into_iter().zip(self.body.params.iter()) { + let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var())); + if let Some(self_param) = self.body.self_param { + if let Some(ty) = param_tys.next() { + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); + self.write_binding_ty(self_param, ty); + } + } + for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index 61638c43d9ce..ff6de61ba649 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -647,7 +647,7 @@ impl InferenceTable<'_> { let goal: InEnvironment = InEnvironment::new(&self.trait_env.env, coerce_unsized_tref.cast(Interner)); - let canonicalized = self.canonicalize(goal); + let canonicalized = self.canonicalize_with_free_vars(goal); // FIXME: rustc's coerce_unsized is more specialized -- it only tries to // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index c377a51e7d3b..a3dab1fd9d54 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -312,15 +312,13 @@ impl InferenceContext<'_> { Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false); - let (res, derefed_callee) = 'b: { - // manual loop to be able to access `derefs.table` - while let Some((callee_deref_ty, _)) = derefs.next() { - let res = derefs.table.callable_sig(&callee_deref_ty, args.len()); - if res.is_some() { - break 'b (res, callee_deref_ty); - } + let (res, derefed_callee) = loop { + let Some((callee_deref_ty, _)) = derefs.next() else { + break (None, callee_ty.clone()); + }; + if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) { + break (Some(res), callee_deref_ty); } - (None, callee_ty.clone()) }; // if the function is unresolved, we use is_varargs=true to // suppress the arg count diagnostic here @@ -657,7 +655,7 @@ impl InferenceContext<'_> { ); } } - if let Some(derefed) = builtin_deref(&mut self.table, &inner_ty, true) { + if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { self.resolve_ty_shallow(derefed) } else { deref_by_trait(&mut self.table, inner_ty) @@ -774,7 +772,7 @@ impl InferenceContext<'_> { let receiver_adjustments = method_resolution::resolve_indexing_op( self.db, self.table.trait_env.clone(), - canonicalized.value, + canonicalized, index_trait, ); let (self_ty, mut adj) = receiver_adjustments @@ -1559,7 +1557,7 @@ impl InferenceContext<'_> { let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); let resolved = method_resolution::lookup_method( self.db, - &canonicalized_receiver.value, + &canonicalized_receiver, self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), VisibleFromModule::Filter(self.resolver.module()), @@ -1608,7 +1606,7 @@ impl InferenceContext<'_> { let resolved = method_resolution::lookup_method( self.db, - &canonicalized_receiver.value, + &canonicalized_receiver, self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), VisibleFromModule::Filter(self.resolver.module()), @@ -1641,7 +1639,7 @@ impl InferenceContext<'_> { }; let assoc_func_with_same_name = method_resolution::iterate_method_candidates( - &canonicalized_receiver.value, + &canonicalized_receiver, self.db, self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 16ae028427d6..8f537bb448b9 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -321,7 +321,7 @@ impl InferenceContext<'_> { let mut not_visible = None; let res = method_resolution::iterate_method_candidates( - &canonical_ty.value, + &canonical_ty, self.db, self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 1d0150d850ff..be7547f9bae5 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -23,12 +23,9 @@ use crate::{ }; impl InferenceContext<'_> { - pub(super) fn canonicalize + HasInterner>( - &mut self, - t: T, - ) -> Canonicalized + pub(super) fn canonicalize(&mut self, t: T) -> Canonical where - T: HasInterner, + T: TypeFoldable + HasInterner, { self.table.canonicalize(t) } @@ -128,14 +125,14 @@ impl> Canonicalized { }), ); for (i, v) in solution.value.iter(Interner).enumerate() { - let var = self.free_vars[i].clone(); + let var = &self.free_vars[i]; if let Some(ty) = v.ty(Interner) { // eagerly replace projections in the type; we may be getting types // e.g. from where clauses where this hasn't happened yet let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner)); ctx.unify(var.assert_ty_ref(Interner), &ty); } else { - let _ = ctx.try_unify(&var, &new_vars.apply(v.clone(), Interner)); + let _ = ctx.try_unify(var, &new_vars.apply(v.clone(), Interner)); } } } @@ -243,7 +240,7 @@ pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc, var_unification_table: ChalkInferenceTable, - type_variable_table: Vec, + type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on /// temporary allocations. @@ -252,8 +249,8 @@ pub(crate) struct InferenceTable<'a> { pub(crate) struct InferenceTableSnapshot { var_table_snapshot: chalk_solve::infer::InferenceSnapshot, + type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, - type_variable_table_snapshot: Vec, } impl<'a> InferenceTable<'a> { @@ -262,7 +259,7 @@ impl<'a> InferenceTable<'a> { db, trait_env, var_unification_table: ChalkInferenceTable::new(), - type_variable_table: Vec::new(), + type_variable_table: SmallVec::new(), pending_obligations: Vec::new(), resolve_obligations_buffer: Vec::new(), } @@ -292,14 +289,14 @@ impl<'a> InferenceTable<'a> { } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { + let is_diverging = self + .type_variable_table + .get(iv.index() as usize) + .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)); + if is_diverging { + return TyKind::Never.intern(Interner); + } match kind { - _ if self - .type_variable_table - .get(iv.index() as usize) - .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)) => - { - TyKind::Never - } TyVariableKind::General => TyKind::Error, TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), @@ -307,12 +304,9 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(crate) fn canonicalize + HasInterner>( - &mut self, - t: T, - ) -> Canonicalized + pub(crate) fn canonicalize_with_free_vars(&mut self, t: T) -> Canonicalized where - T: HasInterner, + T: TypeFoldable + HasInterner, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables @@ -326,6 +320,16 @@ impl<'a> InferenceTable<'a> { Canonicalized { value: result.quantified, free_vars } } + pub(crate) fn canonicalize(&mut self, t: T) -> Canonical + where + T: TypeFoldable + HasInterner, + { + // try to resolve obligations before canonicalizing, since this might + // result in new knowledge about variables + self.resolve_obligations_as_possible(); + self.var_unification_table.canonicalize(Interner, t).quantified + } + /// Recurses through the given type, normalizing associated types mentioned /// in it by replacing them by type variables and registering obligations to /// resolve later. This should be done once for every type we get from some @@ -541,7 +545,7 @@ impl<'a> InferenceTable<'a> { Err(_) => return false, }; result.goals.iter().all(|goal| { - let canonicalized = self.canonicalize(goal.clone()); + let canonicalized = self.canonicalize_with_free_vars(goal.clone()); self.try_resolve_obligation(&canonicalized).is_some() }) } @@ -575,19 +579,15 @@ impl<'a> InferenceTable<'a> { pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot { let var_table_snapshot = self.var_unification_table.snapshot(); - let type_variable_table_snapshot = self.type_variable_table.clone(); + let type_variable_table = self.type_variable_table.clone(); let pending_obligations = self.pending_obligations.clone(); - InferenceTableSnapshot { - var_table_snapshot, - pending_obligations, - type_variable_table_snapshot, - } + InferenceTableSnapshot { var_table_snapshot, pending_obligations, type_variable_table } } #[tracing::instrument(skip_all)] pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) { self.var_unification_table.rollback_to(snapshot.var_table_snapshot); - self.type_variable_table = snapshot.type_variable_table_snapshot; + self.type_variable_table = snapshot.type_variable_table; self.pending_obligations = snapshot.pending_obligations; } @@ -606,7 +606,7 @@ impl<'a> InferenceTable<'a> { let in_env = InEnvironment::new(&self.trait_env.env, goal); let canonicalized = self.canonicalize(in_env); - self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized.value) + self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized) } pub(crate) fn register_obligation(&mut self, goal: Goal) { @@ -615,7 +615,7 @@ impl<'a> InferenceTable<'a> { } fn register_obligation_in_env(&mut self, goal: InEnvironment) { - let canonicalized = self.canonicalize(goal); + let canonicalized = self.canonicalize_with_free_vars(goal); let solution = self.try_resolve_obligation(&canonicalized); if matches!(solution, Some(Solution::Ambig(_))) { self.pending_obligations.push(canonicalized); @@ -798,7 +798,7 @@ impl<'a> InferenceTable<'a> { let trait_data = self.db.trait_data(fn_once_trait); let output_assoc_type = trait_data.associated_type_by_name(&name![Output])?; - let mut arg_tys = vec![]; + let mut arg_tys = Vec::with_capacity(num_args); let arg_ty = TyBuilder::tuple(num_args) .fill(|it| { let arg = match it { @@ -828,11 +828,7 @@ impl<'a> InferenceTable<'a> { environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self - .db - .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner)) - .is_some() - { + if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some() { self.register_obligation(obligation.goal); let return_ty = self.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { @@ -845,7 +841,7 @@ impl<'a> InferenceTable<'a> { let canonical = self.canonicalize(obligation.clone()); if self .db - .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner)) + .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) .is_some() { return Some((fn_x, arg_tys, return_ty)); diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index dea292711d86..9655981cc9cc 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -371,8 +371,8 @@ pub fn layout_of_ty_query( TyKind::Never => cx.layout_of_never_type(), TyKind::Dyn(_) | TyKind::Foreign(_) => { let mut unit = layout_of_unit(&cx, dl)?; - match unit.abi { - Abi::Aggregate { ref mut sized } => *sized = false, + match &mut unit.abi { + Abi::Aggregate { sized } => *sized = false, _ => return Err(LayoutError::Unknown), } unit diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index e68dbe7b02ec..a679a114b4b9 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -213,7 +213,7 @@ impl TraitImpls { // To better support custom derives, collect impls in all unnamed const items. // const _: () = { ... }; - for konst in module_data.scope.unnamed_consts(db.upcast()) { + for konst in module_data.scope.unnamed_consts() { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db.upcast()) { Self::collect_def_map(db, map, &block_def_map); @@ -337,7 +337,7 @@ impl InherentImpls { // To better support custom derives, collect impls in all unnamed const items. // const _: () = { ... }; - for konst in module_data.scope.unnamed_consts(db.upcast()) { + for konst in module_data.scope.unnamed_consts() { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db.upcast()) { self.collect_def_map(db, &block_def_map); @@ -972,10 +972,9 @@ pub fn iterate_method_candidates_dyn( deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| { iterate_method_candidates_with_autoref( - &receiver_ty, + &mut table, + receiver_ty, adj, - db, - env.clone(), traits_in_scope, visible_from_module, name, @@ -1000,10 +999,9 @@ pub fn iterate_method_candidates_dyn( #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_with_autoref( - receiver_ty: &Canonical, + table: &mut InferenceTable<'_>, + receiver_ty: Canonical, first_adjustment: ReceiverAdjustments, - db: &dyn HirDatabase, - env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, @@ -1016,10 +1014,9 @@ fn iterate_method_candidates_with_autoref( let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| { iterate_method_candidates_by_receiver( + table, receiver_ty, first_adjustment, - db, - env.clone(), traits_in_scope, visible_from_module, name, @@ -1034,7 +1031,7 @@ fn iterate_method_candidates_with_autoref( maybe_reborrowed.autoderefs += 1; } - iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?; + iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?; let refed = Canonical { value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone()) @@ -1042,7 +1039,7 @@ fn iterate_method_candidates_with_autoref( binders: receiver_ty.binders.clone(), }; - iterate_method_candidates_by_receiver(&refed, first_adjustment.with_autoref(Mutability::Not))?; + iterate_method_candidates_by_receiver(refed, first_adjustment.with_autoref(Mutability::Not))?; let ref_muted = Canonical { value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) @@ -1050,58 +1047,53 @@ fn iterate_method_candidates_with_autoref( binders: receiver_ty.binders.clone(), }; - iterate_method_candidates_by_receiver( - &ref_muted, - first_adjustment.with_autoref(Mutability::Mut), - ) + iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut)) } #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_by_receiver( - receiver_ty: &Canonical, + table: &mut InferenceTable<'_>, + receiver_ty: Canonical, receiver_adjustments: ReceiverAdjustments, - db: &dyn HirDatabase, - env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { - let mut table = InferenceTable::new(db, env); let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); - let snapshot = table.snapshot(); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. - let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true); - while let Some((self_ty, _)) = autoderef.next() { - iterate_inherent_methods( - &self_ty, - autoderef.table, - name, - Some(&receiver_ty), - Some(receiver_adjustments.clone()), - visible_from_module, - &mut callback, - )? - } - - table.rollback_to(snapshot); - - let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true); - while let Some((self_ty, _)) = autoderef.next() { - iterate_trait_method_candidates( - &self_ty, - autoderef.table, - traits_in_scope, - name, - Some(&receiver_ty), - Some(receiver_adjustments.clone()), - &mut callback, - )? - } - - ControlFlow::Continue(()) + table.run_in_snapshot(|table| { + let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true); + while let Some((self_ty, _)) = autoderef.next() { + iterate_inherent_methods( + &self_ty, + autoderef.table, + name, + Some(&receiver_ty), + Some(receiver_adjustments.clone()), + visible_from_module, + &mut callback, + )? + } + ControlFlow::Continue(()) + })?; + table.run_in_snapshot(|table| { + let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true); + while let Some((self_ty, _)) = autoderef.next() { + iterate_trait_method_candidates( + &self_ty, + autoderef.table, + traits_in_scope, + name, + Some(&receiver_ty), + Some(receiver_adjustments.clone()), + &mut callback, + )? + } + ControlFlow::Continue(()) + }) } #[tracing::instrument(skip_all, fields(name = ?name))] @@ -1147,9 +1139,9 @@ fn iterate_trait_method_candidates( callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; - let env = table.trait_env.clone(); - let canonical_self_ty = table.canonicalize(self_ty.clone()).value; + let canonical_self_ty = table.canonicalize(self_ty.clone()); + let TraitEnvironment { krate, block, .. } = *table.trait_env; 'traits: for &t in traits_in_scope { let data = db.trait_data(t); @@ -1164,7 +1156,7 @@ fn iterate_trait_method_candidates( { // FIXME: this should really be using the edition of the method name's span, in case it // comes from a macro - if db.crate_graph()[env.krate].edition < Edition::Edition2021 { + if db.crate_graph()[krate].edition < Edition::Edition2021 { continue; } } @@ -1183,8 +1175,8 @@ fn iterate_trait_method_candidates( IsValidCandidate::No => continue, }; if !known_implemented { - let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty); - if db.trait_solve(env.krate, env.block, goal.cast(Interner)).is_none() { + let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty); + if db.trait_solve(krate, block, goal.cast(Interner)).is_none() { continue 'traits; } } @@ -1365,7 +1357,7 @@ pub(crate) fn resolve_indexing_op( let ty = table.instantiate_canonical(ty); let deref_chain = autoderef_method_receiver(&mut table, ty); for (ty, adj) in deref_chain { - let goal = generic_implements_goal(db, table.trait_env.clone(), index_trait, &ty); + let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty); if db .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner)) .is_some() @@ -1548,7 +1540,7 @@ fn is_valid_impl_fn_candidate( for goal in goals.clone() { let in_env = InEnvironment::new(&table.trait_env.env, goal); - let canonicalized = table.canonicalize(in_env); + let canonicalized = table.canonicalize_with_free_vars(in_env); let solution = table.db.trait_solve( table.trait_env.krate, table.trait_env.block, @@ -1586,10 +1578,10 @@ fn is_valid_impl_fn_candidate( pub fn implements_trait( ty: &Canonical, db: &dyn HirDatabase, - env: Arc, + env: &TraitEnvironment, trait_: TraitId, ) -> bool { - let goal = generic_implements_goal(db, env.clone(), trait_, ty); + let goal = generic_implements_goal(db, env, trait_, ty); let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); solution.is_some() @@ -1598,10 +1590,10 @@ pub fn implements_trait( pub fn implements_trait_unique( ty: &Canonical, db: &dyn HirDatabase, - env: Arc, + env: &TraitEnvironment, trait_: TraitId, ) -> bool { - let goal = generic_implements_goal(db, env.clone(), trait_, ty); + let goal = generic_implements_goal(db, env, trait_, ty); let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); matches!(solution, Some(crate::Solution::Unique(_))) @@ -1612,32 +1604,34 @@ pub fn implements_trait_unique( #[tracing::instrument(skip_all)] fn generic_implements_goal( db: &dyn HirDatabase, - env: Arc, + env: &TraitEnvironment, trait_: TraitId, self_ty: &Canonical, ) -> Canonical> { - let mut kinds = self_ty.binders.interned().to_vec(); + let binders = self_ty.binders.interned(); let trait_ref = TyBuilder::trait_ref(db, trait_) .push(self_ty.value.clone()) - .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) + .fill_with_bound_vars(DebruijnIndex::INNERMOST, binders.len()) .build(); - kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|it| { - let vk = match it.data(Interner) { - chalk_ir::GenericArgData::Ty(_) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, - chalk_ir::GenericArgData::Const(c) => { - chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) - } - }; - chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) - })); + + let kinds = + binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| { + let vk = match it.data(Interner) { + chalk_ir::GenericArgData::Ty(_) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) + } + chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, + chalk_ir::GenericArgData::Const(c) => { + chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) + } + }; + chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) + })); + let binders = CanonicalVarKinds::from_iter(Interner, kinds); + let obligation = trait_ref.cast(Interner); - Canonical { - binders: CanonicalVarKinds::from_iter(Interner, kinds), - value: InEnvironment::new(&env.env, obligation), - } + let value = InEnvironment::new(&env.env, obligation); + Canonical { binders, value } } fn autoderef_method_receiver( @@ -1648,7 +1642,7 @@ fn autoderef_method_receiver( let mut autoderef = autoderef::Autoderef::new(table, ty, false); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( - autoderef.table.canonicalize(ty).value, + autoderef.table.canonicalize(ty), ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false }, )); } diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index cfaef2a392c8..d51335503771 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -1165,6 +1165,7 @@ impl MirBody { pub enum MirSpan { ExprId(ExprId), PatId(PatId), + SelfParam, Unknown, } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 2428678d72b5..fd98141af63e 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -376,6 +376,10 @@ impl MirEvalError { Ok(s) => s.map(|it| it.syntax_node_ptr()), Err(_) => continue, }, + MirSpan::SelfParam => match source_map.self_param_syntax() { + Some(s) => s.map(|it| it.syntax_node_ptr()), + None => continue, + }, MirSpan::Unknown => continue, }; let file_id = span.file_id.original_file(db.upcast()); diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index d0f739e6ac66..7e582c03efcd 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1810,9 +1810,20 @@ impl<'ctx> MirLowerCtx<'ctx> { fn lower_params_and_bindings( &mut self, params: impl Iterator + Clone, + self_binding: Option<(BindingId, Ty)>, pick_binding: impl Fn(BindingId) -> bool, ) -> Result { let base_param_count = self.result.param_locals.len(); + let self_binding = match self_binding { + Some((self_binding, ty)) => { + let local_id = self.result.locals.alloc(Local { ty }); + self.drop_scopes.last_mut().unwrap().locals.push(local_id); + self.result.binding_locals.insert(self_binding, local_id); + self.result.param_locals.push(local_id); + Some(self_binding) + } + None => None, + }; self.result.param_locals.extend(params.clone().map(|(it, ty)| { let local_id = self.result.locals.alloc(Local { ty }); self.drop_scopes.last_mut().unwrap().locals.push(local_id); @@ -1838,9 +1849,23 @@ impl<'ctx> MirLowerCtx<'ctx> { } } let mut current = self.result.start_block; - for ((param, _), local) in - params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count)) - { + if let Some(self_binding) = self_binding { + let local = self.result.param_locals.clone()[base_param_count]; + if local != self.binding_local(self_binding)? { + let r = self.match_self_param(self_binding, current, local)?; + if let Some(b) = r.1 { + self.set_terminator(b, TerminatorKind::Unreachable, MirSpan::SelfParam); + } + current = r.0; + } + } + let local_params = self + .result + .param_locals + .clone() + .into_iter() + .skip(base_param_count + self_binding.is_some() as usize); + for ((param, _), local) in params.zip(local_params) { if let Pat::Bind { id, .. } = self.body[param] { if local == self.binding_local(id)? { continue; @@ -2019,6 +2044,7 @@ pub fn mir_body_for_closure_query( }; let current = ctx.lower_params_and_bindings( args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())), + None, |_| true, )?; if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { @@ -2149,16 +2175,16 @@ pub fn lower_to_mir( let substs = TyBuilder::placeholder_subst(db, fid); let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs); + let mut params = callable_sig.params().iter(); + let self_param = body.self_param.and_then(|id| Some((id, params.next()?.clone()))); break 'b ctx.lower_params_and_bindings( - body.params - .iter() - .zip(callable_sig.params().iter()) - .map(|(it, y)| (*it, y.clone())), + body.params.iter().zip(params).map(|(it, y)| (*it, y.clone())), + self_param, binding_picker, )?; } } - ctx.lower_params_and_bindings([].into_iter(), binding_picker)? + ctx.lower_params_and_bindings([].into_iter(), None, binding_picker)? }; if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?; diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 90cbd13a6c62..759690679434 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -11,7 +11,7 @@ use crate::{ Substitution, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind, ValueNs, VariantData, VariantId, }, - MutBorrowKind, + LocalId, MutBorrowKind, }, BindingMode, }; @@ -82,6 +82,22 @@ impl MirLowerCtx<'_> { Ok((current, current_else)) } + pub(super) fn match_self_param( + &mut self, + id: BindingId, + current: BasicBlockId, + local: LocalId, + ) -> Result<(BasicBlockId, Option)> { + self.pattern_match_binding( + id, + BindingMode::Move, + local.into(), + MirSpan::SelfParam, + current, + None, + ) + } + fn pattern_match_inner( &mut self, mut current: BasicBlockId, @@ -283,9 +299,9 @@ impl MirLowerCtx<'_> { (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } - if let Some(slice) = slice { + if let &Some(slice) = slice { if mode == MatchingMode::Bind { - if let Pat::Bind { id, subpat: _ } = self.body[*slice] { + if let Pat::Bind { id, subpat: _ } = self.body[slice] { let next_place = cond_place.project( ProjectionElem::Subslice { from: prefix.len() as u64, @@ -293,11 +309,12 @@ impl MirLowerCtx<'_> { }, &mut self.result.projection_store, ); + let mode = self.infer.binding_modes[slice]; (current, current_else) = self.pattern_match_binding( id, - *slice, + mode, next_place, - (*slice).into(), + (slice).into(), current, current_else, )?; @@ -398,9 +415,10 @@ impl MirLowerCtx<'_> { self.pattern_match_inner(current, current_else, cond_place, *subpat, mode)? } if mode == MatchingMode::Bind { + let mode = self.infer.binding_modes[pattern]; self.pattern_match_binding( *id, - pattern, + mode, cond_place, pattern.into(), current, @@ -437,14 +455,13 @@ impl MirLowerCtx<'_> { fn pattern_match_binding( &mut self, id: BindingId, - pat: PatId, + mode: BindingMode, cond_place: Place, span: MirSpan, current: BasicBlockId, current_else: Option, ) -> Result<(BasicBlockId, Option)> { let target_place = self.binding_local(id)?; - let mode = self.infer.binding_modes[pat]; self.push_storage_live(id, current)?; self.push_assignment( current, diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 5e159236f488..d699067b5a64 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -12,7 +12,7 @@ mod traits; use std::env; -use base_db::{FileRange, SourceDatabaseExt}; +use base_db::{FileRange, SourceDatabaseExt2 as _}; use expect_test::Expect; use hir_def::{ body::{Body, BodySourceMap, SyntheticSyntax}, @@ -164,7 +164,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour Some(value) => value, None => continue, }; - let range = node.as_ref().original_file_range(&db); + let range = node.as_ref().original_file_range_rooted(&db); if let Some(expected) = types.remove(&range) { let actual = if display_source { ty.display_source_code(&db, def.module(&db), true).unwrap() @@ -180,7 +180,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour Some(value) => value, None => continue, }; - let range = node.as_ref().original_file_range(&db); + let range = node.as_ref().original_file_range_rooted(&db); if let Some(expected) = types.remove(&range) { let actual = if display_source { ty.display_source_code(&db, def.module(&db), true).unwrap() @@ -211,7 +211,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour }) else { continue; }; - let range = node.as_ref().original_file_range(&db); + let range = node.as_ref().original_file_range_rooted(&db); let actual = format!( "expected {}, got {}", mismatch.expected.display_test(&db), @@ -293,20 +293,29 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let mut types: Vec<(InFile, &Ty)> = Vec::new(); let mut mismatches: Vec<(InFile, &TypeMismatch)> = Vec::new(); + if let Some(self_param) = body.self_param { + let ty = &inference_result.type_of_binding[self_param]; + if let Some(syntax_ptr) = body_source_map.self_param_syntax() { + let root = db.parse_or_expand(syntax_ptr.file_id); + let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); + types.push((node.clone(), ty)); + } + } + for (pat, mut ty) in inference_result.type_of_pat.iter() { if let Pat::Bind { id, .. } = body.pats[pat] { ty = &inference_result.type_of_binding[id]; } - let syntax_ptr = match body_source_map.pat_syntax(pat) { + let node = match body_source_map.pat_syntax(pat) { Ok(sp) => { let root = db.parse_or_expand(sp.file_id); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => continue, }; - types.push((syntax_ptr.clone(), ty)); + types.push((node.clone(), ty)); if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) { - mismatches.push((syntax_ptr, mismatch)); + mismatches.push((node, mismatch)); } } @@ -575,7 +584,7 @@ fn salsa_bug() { } "; - db.set_file_text(pos.file_id, Arc::from(new_text)); + db.set_file_text(pos.file_id, new_text); let module = db.module_for_file(pos.file_id); let crate_def_map = module.def_map(&db); diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs index 82d934009f36..6066ec69c9a3 100644 --- a/crates/hir-ty/src/tests/incremental.rs +++ b/crates/hir-ty/src/tests/incremental.rs @@ -1,6 +1,5 @@ -use base_db::SourceDatabaseExt; +use base_db::SourceDatabaseExt2 as _; use test_fixture::WithFixture; -use triomphe::Arc; use crate::{db::HirDatabase, test_db::TestDB}; @@ -33,7 +32,7 @@ fn foo() -> i32 { 1 }"; - db.set_file_text(pos.file_id, Arc::from(new_text)); + db.set_file_text(pos.file_id, new_text); { let events = db.log_executed(|| { @@ -85,7 +84,7 @@ fn baz() -> i32 { } "; - db.set_file_text(pos.file_id, Arc::from(new_text)); + db.set_file_text(pos.file_id, new_text); { let events = db.log_executed(|| { diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index c837fae3fef4..8609ba410394 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -1461,28 +1461,6 @@ fn f() { ); } -#[test] -fn trait_impl_in_synstructure_const() { - check_types( - r#" -struct S; - -trait Tr { - fn method(&self) -> u16; -} - -const _DERIVE_Tr_: () = { - impl Tr for S {} -}; - -fn f() { - S.method(); - //^^^^^^^^^^ u16 -} - "#, - ); -} - #[test] fn inherent_impl_in_unnamed_const() { check_types( @@ -1795,6 +1773,21 @@ fn test() { ); } +#[test] +fn deref_into_inference_var() { + check_types( + r#" +//- minicore:deref +struct A(T); +impl core::ops::Deref for A {} +impl A { fn foo(&self) {} } +fn main() { + A(0).foo(); + //^^^^^^^^^^ () +} +"#, + ); +} #[test] fn receiver_adjustment_autoref() { check( diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index ffd6a6051b93..917e9f440852 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -2121,6 +2121,7 @@ async fn main() { "#, expect![[r#" 16..193 '{ ...2 }; }': () + 16..193 '{ ...2 }; }': impl Future 26..27 'x': i32 30..43 'unsafe { 92 }': i32 39..41 '92': i32 @@ -2131,6 +2132,8 @@ async fn main() { 73..75 '()': () 95..96 'z': ControlFlow<(), ()> 130..140 'try { () }': ControlFlow<(), ()> + 130..140 'try { () }': fn from_output>( as Try>::Output) -> ControlFlow<(), ()> + 130..140 'try { () }': ControlFlow<(), ()> 136..138 '()': () 150..151 'w': i32 154..166 'const { 92 }': i32 diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index fa9fe4953edd..4518422d27e9 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -204,7 +204,7 @@ pub struct NoSuchField { #[derive(Debug)] pub struct PrivateAssocItem { - pub expr_or_pat: InFile>>>, + pub expr_or_pat: InFile>>, pub item: AssocItem, } @@ -240,7 +240,7 @@ pub struct UnresolvedMethodCall { #[derive(Debug)] pub struct UnresolvedAssocItem { - pub expr_or_pat: InFile>>>, + pub expr_or_pat: InFile>>, } #[derive(Debug)] diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index cdc0db8653c1..c5d44c11f2c1 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -159,6 +159,7 @@ impl HirDisplay for Adt { impl HirDisplay for Struct { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { let module_id = self.module(f.db).id; + // FIXME: Render repr if its set explicitly? write_visibility(module_id, self.visibility(f.db), f)?; f.write_str("struct ")?; write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; @@ -166,37 +167,40 @@ impl HirDisplay for Struct { write_generic_params(def_id, f)?; let variant_data = self.variant_data(f.db); - if let StructKind::Tuple = variant_data.kind() { - f.write_char('(')?; - let mut it = variant_data.fields().iter().peekable(); + match variant_data.kind() { + StructKind::Tuple => { + f.write_char('(')?; + let mut it = variant_data.fields().iter().peekable(); - while let Some((id, _)) = it.next() { - let field = Field { parent: (*self).into(), id }; - write_visibility(module_id, field.visibility(f.db), f)?; - field.ty(f.db).hir_fmt(f)?; - if it.peek().is_some() { - f.write_str(", ")?; + while let Some((id, _)) = it.next() { + let field = Field { parent: (*self).into(), id }; + write_visibility(module_id, field.visibility(f.db), f)?; + field.ty(f.db).hir_fmt(f)?; + if it.peek().is_some() { + f.write_str(", ")?; + } + } + + f.write_char(')')?; + write_where_clause(def_id, f)?; + } + StructKind::Record => { + let has_where_clause = write_where_clause(def_id, f)?; + let fields = self.fields(f.db); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if fields.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{\n")?; + for field in self.fields(f.db) { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + f.write_str("}")?; } } - - f.write_str(");")?; - } - - write_where_clause(def_id, f)?; - - if let StructKind::Record = variant_data.kind() { - let fields = self.fields(f.db); - if fields.is_empty() { - f.write_str(" {}")?; - } else { - f.write_str(" {\n")?; - for field in self.fields(f.db) { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; - } - f.write_str("}")?; - } + StructKind::Unit => _ = write_where_clause(def_id, f)?, } Ok(()) @@ -210,11 +214,12 @@ impl HirDisplay for Enum { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; - write_where_clause(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; let variants = self.variants(f.db); if !variants.is_empty() { - f.write_str(" {\n")?; + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + f.write_str("{\n")?; for variant in variants { f.write_str(" ")?; variant.hir_fmt(f)?; @@ -234,11 +239,12 @@ impl HirDisplay for Union { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; - write_where_clause(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; let fields = self.fields(f.db); if !fields.is_empty() { - f.write_str(" {\n")?; + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + f.write_str("{\n")?; for field in self.fields(f.db) { f.write_str(" ")?; field.hir_fmt(f)?; @@ -446,7 +452,10 @@ fn write_generic_params( Ok(()) } -fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +fn write_where_clause( + def: GenericDefId, + f: &mut HirFormatter<'_>, +) -> Result { let params = f.db.generic_params(def); // unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`. @@ -465,7 +474,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), }); if !has_displayable_predicate { - return Ok(()); + return Ok(false); } let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target { @@ -543,7 +552,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), // End of final predicate. There must be at least one predicate here. f.write_char(',')?; - Ok(()) + Ok(true) } impl HirDisplay for Const { @@ -594,19 +603,20 @@ impl HirDisplay for Trait { write!(f, "trait {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TraitId(self.id); write_generic_params(def_id, f)?; - write_where_clause(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { let assoc_items = self.items(f.db); let count = assoc_items.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; if count == 0 { if assoc_items.is_empty() { - f.write_str(" {}")?; + f.write_str("{}")?; } else { - f.write_str(" { /* … */ }")?; + f.write_str("{ /* … */ }")?; } } else { - f.write_str(" {\n")?; + f.write_str("{\n")?; for item in &assoc_items[..count] { f.write_str(" ")?; match item { @@ -651,7 +661,6 @@ impl HirDisplay for TypeAlias { write!(f, "type {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TypeAliasId(self.id); write_generic_params(def_id, f)?; - write_where_clause(def_id, f)?; if !data.bounds.is_empty() { f.write_str(": ")?; f.write_joined(data.bounds.iter(), " + ")?; @@ -660,6 +669,7 @@ impl HirDisplay for TypeAlias { f.write_str(" = ")?; ty.hir_fmt(f)?; } + write_where_clause(def_id, f)?; Ok(()) } } diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index d10884517f92..7cdcdd76d185 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -9,6 +9,7 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile}; use syntax::ast; +use tt::TextRange; use crate::{ db::HirDatabase, Adt, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl, @@ -37,6 +38,12 @@ impl Module { def_map[self.id.local_id].definition_source(db.upcast()) } + /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. + pub fn definition_source_range(self, db: &dyn HirDatabase) -> InFile { + let def_map = self.id.def_map(db.upcast()); + def_map[self.id.local_id].definition_source_range(db.upcast()) + } + pub fn definition_source_file_id(self, db: &dyn HirDatabase) -> HirFileId { let def_map = self.id.def_map(db.upcast()); def_map[self.id.local_id].definition_source_file_id() @@ -71,6 +78,13 @@ impl Module { let def_map = self.id.def_map(db.upcast()); def_map[self.id.local_id].declaration_source(db.upcast()) } + + /// Returns a text range which declares this module, either a `mod foo;` or a `mod foo {}`. + /// `None` for the crate root. + pub fn declaration_source_range(self, db: &dyn HirDatabase) -> Option> { + let def_map = self.id.def_map(db.upcast()); + def_map[self.id.local_id].declaration_source_range(db.upcast()) + } } impl HasSource for Field { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5eed7ecd5b21..b922aa8e46db 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -56,8 +56,8 @@ use hir_def::{ AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, - MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, - TypeOrConstParamId, TypeParamId, UnionId, + ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId, + TypeParamId, UnionId, }; use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind}; use hir_ty::{ @@ -122,7 +122,7 @@ pub use { visibility::Visibility, // FIXME: This is here since some queries take it as input that are used // outside of hir. - {AdtId, ModuleDefId}, + {AdtId, MacroId, ModuleDefId}, }, hir_expand::{ attrs::{Attr, AttrId}, @@ -754,7 +754,7 @@ impl Module { scope .declarations() .map(ModuleDef::from) - .chain(scope.unnamed_consts(db.upcast()).map(|id| ModuleDef::Const(Const::from(id)))) + .chain(scope.unnamed_consts().map(|id| ModuleDef::Const(Const::from(id)))) .collect() } @@ -1725,6 +1725,10 @@ impl DefWithBody { Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, + mir::MirSpan::SelfParam => match source_map.self_param_syntax() { + Some(s) => s.map(|it| it.into()), + None => continue, + }, mir::MirSpan::Unknown => continue, }; acc.push( @@ -1776,6 +1780,11 @@ impl DefWithBody { Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, + mir::MirSpan::SelfParam => match source_map.self_param_syntax() + { + Some(s) => s.map(|it| it.into()), + None => continue, + }, mir::MirSpan::Unknown => continue, }; acc.push(NeedMut { local, span }.into()); @@ -2127,8 +2136,11 @@ impl Param { pub fn as_local(&self, db: &dyn HirDatabase) -> Option { let parent = DefWithBodyId::FunctionId(self.func.into()); let body = db.body(parent); - let pat_id = body.params[self.idx]; - if let Pat::Bind { id, .. } = &body[pat_id] { + if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { + Some(Local { parent, binding_id: self_param }) + } else if let Pat::Bind { id, .. } = + &body[body.params[self.idx - body.self_param.is_some() as usize]] + { Some(Local { parent, binding_id: *id }) } else { None @@ -2143,7 +2155,7 @@ impl Param { let InFile { file_id, value } = self.func.source(db)?; let params = value.param_list()?; if params.self_param().is_some() { - params.params().nth(self.idx.checked_sub(1)?) + params.params().nth(self.idx.checked_sub(params.self_param().is_some() as usize)?) } else { params.params().nth(self.idx) } @@ -2605,6 +2617,15 @@ impl Macro { } } + pub fn is_env_or_option_env(&self, db: &dyn HirDatabase) -> bool { + match self.id { + MacroId::Macro2Id(it) => { + matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env()) + } + MacroId::MacroRulesId(_) | MacroId::ProcMacroId(_) => false, + } + } + pub fn is_attr(&self, db: &dyn HirDatabase) -> bool { matches!(self.kind(db), MacroKind::Attr) } @@ -3134,35 +3155,59 @@ impl Local { /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` pub fn sources(self, db: &dyn HirDatabase) -> Vec { let (body, source_map) = db.body_with_source_map(self.parent); - self.sources_(db, &body, &source_map).collect() + match body.self_param.zip(source_map.self_param_syntax()) { + Some((param, source)) if param == self.binding_id => { + let root = source.file_syntax(db.upcast()); + vec![LocalSource { + local: self, + source: source.map(|ast| Either::Right(ast.to_node(&root))), + }] + } + _ => body[self.binding_id] + .definitions + .iter() + .map(|&definition| { + let src = source_map.pat_syntax(definition).unwrap(); // Hmm... + let root = src.file_syntax(db.upcast()); + LocalSource { + local: self, + source: src.map(|ast| match ast.to_node(&root) { + ast::Pat::IdentPat(it) => Either::Left(it), + _ => unreachable!("local with non ident-pattern"), + }), + } + }) + .collect(), + } } /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = it;` pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource { let (body, source_map) = db.body_with_source_map(self.parent); - let src = self.sources_(db, &body, &source_map).next().unwrap(); - src - } - - fn sources_<'a>( - self, - db: &'a dyn HirDatabase, - body: &'a hir_def::body::Body, - source_map: &'a hir_def::body::BodySourceMap, - ) -> impl Iterator + 'a { - body[self.binding_id] - .definitions - .iter() - .map(|&definition| { - let src = source_map.pat_syntax(definition).unwrap(); // Hmm... - let root = src.file_syntax(db.upcast()); - src.map(|ast| match ast.to_node(&root) { - Either::Left(ast::Pat::IdentPat(it)) => Either::Left(it), - Either::Left(_) => unreachable!("local with non ident-pattern"), - Either::Right(it) => Either::Right(it), + match body.self_param.zip(source_map.self_param_syntax()) { + Some((param, source)) if param == self.binding_id => { + let root = source.file_syntax(db.upcast()); + LocalSource { + local: self, + source: source.map(|ast| Either::Right(ast.to_node(&root))), + } + } + _ => body[self.binding_id] + .definitions + .first() + .map(|&definition| { + let src = source_map.pat_syntax(definition).unwrap(); // Hmm... + let root = src.file_syntax(db.upcast()); + LocalSource { + local: self, + source: src.map(|ast| match ast.to_node(&root) { + ast::Pat::IdentPat(it) => Either::Left(it), + _ => unreachable!("local with non ident-pattern"), + }), + } }) - }) - .map(move |source| LocalSource { local: self, source }) + .unwrap(), + } } } @@ -4037,7 +4082,7 @@ impl Type { let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), trait_) + method_resolution::implements_trait(&canonical_ty, db, &self.env, trait_) } /// Checks that particular type `ty` implements `std::ops::FnOnce`. @@ -4052,12 +4097,7 @@ impl Type { let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait_unique( - &canonical_ty, - db, - self.env.clone(), - fnonce_trait, - ) + method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, fnonce_trait) } // FIXME: Find better API that also handles const generics diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 99907ea15b50..9796009cb45c 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -681,28 +681,29 @@ impl<'db> SemanticsImpl<'db> { .filter(|&(_, include_file_id)| include_file_id == file_id) { let macro_file = invoc.as_macro_file(); - let expansion_info = cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(self.db.upcast())); + let expansion_info = cache.entry(macro_file).or_insert_with(|| { + let exp_info = macro_file.expansion_info(self.db.upcast()); + + let InMacroFile { file_id, value } = exp_info.expanded(); + self.cache(value, file_id.into()); + + exp_info + }); // Create the source analyzer for the macro call scope let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file())) else { continue; }; - { - let InMacroFile { file_id: macro_file, value } = expansion_info.expanded(); - self.cache(value, macro_file.into()); - } // get mapped token in the include! macro file - let span = span::SpanData { + let span = span::Span { range: token.text_range(), anchor: span::SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID }, ctx: SyntaxContextId::ROOT, }; let Some(InMacroFile { file_id, value: mut mapped_tokens }) = - expansion_info.map_range_down(span) + expansion_info.map_range_down_exact(span) else { continue; }; @@ -753,22 +754,20 @@ impl<'db> SemanticsImpl<'db> { let def_map = sa.resolver.def_map(); let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])]; - let mut process_expansion_for_token = |stack: &mut Vec<_>, macro_file| { - let expansion_info = cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(self.db.upcast())); + let exp_info = cache.entry(macro_file).or_insert_with(|| { + let exp_info = macro_file.expansion_info(self.db.upcast()); - { - let InMacroFile { file_id, value } = expansion_info.expanded(); + let InMacroFile { file_id, value } = exp_info.expanded(); self.cache(value, file_id.into()); - } - let InMacroFile { file_id, value: mapped_tokens } = - expansion_info.map_range_down(span)?; + exp_info + }); + + let InMacroFile { file_id, value: mapped_tokens } = exp_info.map_range_down(span)?; let mapped_tokens: SmallVec<[_; 2]> = mapped_tokens.collect(); - // if the length changed we have found a mapping for the token + // we have found a mapping for the token if the vec is non-empty let res = mapped_tokens.is_empty().not().then_some(()); // requeue the tokens we got from mapping our current token down stack.push((HirFileId::from(file_id), mapped_tokens)); @@ -851,7 +850,13 @@ impl<'db> SemanticsImpl<'db> { // remove any other token in this macro input, all their mappings are the // same as this one tokens.retain(|t| !text_range.contains_range(t.text_range())); - process_expansion_for_token(&mut stack, file_id) + + process_expansion_for_token(&mut stack, file_id).or(file_id + .eager_arg(self.db.upcast()) + .and_then(|arg| { + // also descend into eager expansions + process_expansion_for_token(&mut stack, arg.as_macro_file()) + })) } else if let Some(meta) = ast::Meta::cast(parent) { // attribute we failed expansion for earlier, this might be a derive invocation // or derive helper attribute @@ -960,7 +965,7 @@ impl<'db> SemanticsImpl<'db> { /// macro file the node resides in. pub fn original_range(&self, node: &SyntaxNode) -> FileRange { let node = self.find_file(node); - node.original_file_range(self.db.upcast()) + node.original_file_range_rooted(self.db.upcast()) } /// Attempts to map the node out of macro expanded files returning the original file range. @@ -984,9 +989,9 @@ impl<'db> SemanticsImpl<'db> { /// Attempts to map the node out of macro expanded files. /// This only work for attribute expansions, as other ones do not have nodes as input. - pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option { + pub fn original_syntax_node_rooted(&self, node: &SyntaxNode) -> Option { let InFile { file_id, .. } = self.find_file(node); - InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map( + InFile::new(file_id, node).original_syntax_node_rooted(self.db.upcast()).map( |InRealFile { file_id, value }| { self.cache(find_root(&value), file_id.into()); value @@ -997,7 +1002,7 @@ impl<'db> SemanticsImpl<'db> { pub fn diagnostics_display_range(&self, src: InFile) -> FileRange { let root = self.parse_or_expand(src.file_id); let node = src.map(|it| it.to_node(&root)); - node.as_ref().original_file_range(self.db.upcast()) + node.as_ref().original_file_range_rooted(self.db.upcast()) } fn token_ancestors_with_macros( @@ -1236,6 +1241,11 @@ impl<'db> SemanticsImpl<'db> { sa.resolve_macro_call(self.db, macro_call) } + pub fn is_proc_macro_call(&self, macro_call: &ast::MacroCall) -> bool { + self.resolve_macro_call(macro_call) + .map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..))) + } + pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { let sa = match self.analyze(macro_call.syntax()) { Some(it) => it, diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 4733ea5a35b9..d4d6f0b243fd 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -101,7 +101,7 @@ use hir_def::{ use hir_expand::{attrs::AttrId, name::AsName, HirFileId, HirFileIdExt, MacroCallId}; use rustc_hash::FxHashMap; use smallvec::SmallVec; -use stdx::{impl_from, never}; +use stdx::impl_from; use syntax::{ ast::{self, HasName}, AstNode, SyntaxNode, @@ -253,14 +253,8 @@ impl SourceToDefCtx<'_, '_> { src: InFile, ) -> Option<(DefWithBodyId, BindingId)> { let container = self.find_pat_or_label_container(src.syntax())?; - let (body, source_map) = self.db.body_with_source_map(container); - let pat_id = source_map.node_self_param(src.as_ref())?; - if let crate::Pat::Bind { id, .. } = body[pat_id] { - Some((container, id)) - } else { - never!(); - None - } + let body = self.db.body(container); + Some((container, body.self_param?)) } pub(super) fn label_to_def( &mut self, diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index f87e0a3897af..dc96a1b03d0c 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -219,11 +219,10 @@ impl SourceAnalyzer { pub(crate) fn type_of_self( &self, db: &dyn HirDatabase, - param: &ast::SelfParam, + _param: &ast::SelfParam, ) -> Option { - let src = InFile { file_id: self.file_id, value: param }; - let pat_id = self.body_source_map()?.node_self_param(src)?; - let ty = self.infer.as_ref()?[pat_id].clone(); + let binding = self.body()?.self_param?; + let ty = self.infer.as_ref()?[binding].clone(); Some(Type::new_with_resolver(db, &self.resolver, ty)) } diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 28ac5940e692..3b88836c24bd 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -49,7 +49,7 @@ impl DeclarationLocation { return FileRange { file_id, range: self.ptr.text_range() }; } let node = resolve_node(db, self.hir_file_id, &self.ptr); - node.as_ref().original_file_range(db.upcast()) + node.as_ref().original_file_range_rooted(db.upcast()) } } @@ -165,7 +165,6 @@ impl<'a> SymbolCollector<'a> { // Record renamed imports. // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily // for now. - // FIXME: This parses! for id in scope.imports() { let source = id.import.child_source(self.db.upcast()); let Some(use_tree_src) = source.value.get(id.idx) else { continue }; @@ -196,7 +195,7 @@ impl<'a> SymbolCollector<'a> { }); } - for const_id in scope.unnamed_consts(self.db.upcast()) { + for const_id in scope.unnamed_consts() { self.collect_from_body(const_id); } diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index af834c8a53db..42f935651cf5 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -1,5 +1,6 @@ use std::iter; +use either::Either; use hir::{HasSource, HirFileIdExt, ModuleSource}; use ide_db::{ assists::{AssistId, AssistKind}, @@ -10,17 +11,16 @@ use ide_db::{ }; use itertools::Itertools; use smallvec::SmallVec; -use stdx::format_to; use syntax::{ algo::find_node_at_range, ast::{ self, edit::{AstNodeEdit, IndentLevel}, - make, HasName, HasVisibility, + make, HasVisibility, }, - match_ast, ted, AstNode, SourceFile, + match_ast, ted, AstNode, SyntaxKind::{self, WHITESPACE}, - SyntaxNode, TextRange, + SyntaxNode, TextRange, TextSize, }; use crate::{AssistContext, Assists}; @@ -109,76 +109,35 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti //We are getting item usages and record_fields together, record_fields //for change_visibility and usages for first point mentioned above in the process - let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx); + + let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) = + module.get_usages_and_record_fields(ctx); + + builder.edit_file(ctx.file_id()); + use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| { + builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}")); + }); let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx); module.change_visibility(record_fields); - let mut body_items: Vec = Vec::new(); - let mut items_to_be_processed: Vec = module.body_items.clone(); + let module_def = generate_module_def(&impl_parent, &mut module, old_item_indent); - let new_item_indent = if impl_parent.is_some() { - old_item_indent + 2 - } else { - items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat(); - old_item_indent + 1 - }; - - for item in items_to_be_processed { - let item = item.indent(IndentLevel(1)); - let mut indented_item = String::new(); - format_to!(indented_item, "{new_item_indent}{item}"); - body_items.push(indented_item); - } - - let mut body = body_items.join("\n\n"); - - if let Some(impl_) = &impl_parent { - let mut impl_body_def = String::new(); - - if let Some(self_ty) = impl_.self_ty() { - { - let impl_indent = old_item_indent + 1; - format_to!( - impl_body_def, - "{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}", - ); - } - body = impl_body_def; - - // Add the import for enum/struct corresponding to given impl block - module.make_use_stmt_of_node_with_super(self_ty.syntax()); - for item in module.use_items { - let item_indent = old_item_indent + 1; - body = format!("{item_indent}{item}\n\n{body}"); - } - } - } - - let mut module_def = String::new(); - - let module_name = module.name; - format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}"); - - let mut usages_to_be_updated_for_curr_file = vec![]; - for usages_to_be_updated_for_file in usages_to_be_processed { - if usages_to_be_updated_for_file.0 == ctx.file_id() { - usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1; + let mut usages_to_be_processed_for_cur_file = vec![]; + for (file_id, usages) in usages_to_be_processed { + if file_id == ctx.file_id() { + usages_to_be_processed_for_cur_file = usages; continue; } - builder.edit_file(usages_to_be_updated_for_file.0); - for usage_to_be_processed in usages_to_be_updated_for_file.1 { - builder.replace(usage_to_be_processed.0, usage_to_be_processed.1) + builder.edit_file(file_id); + for (text_range, usage) in usages { + builder.replace(text_range, usage) } } builder.edit_file(ctx.file_id()); - for usage_to_be_processed in usages_to_be_updated_for_curr_file { - builder.replace(usage_to_be_processed.0, usage_to_be_processed.1) - } - - for import_path_text_range in import_paths_to_be_removed { - builder.delete(import_path_text_range); + for (text_range, usage) in usages_to_be_processed_for_cur_file { + builder.replace(text_range, usage); } if let Some(impl_) = impl_parent { @@ -199,12 +158,51 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); } else { + for import_path_text_range in import_paths_to_be_removed { + if module.text_range.intersect(import_path_text_range).is_some() { + module.text_range = module.text_range.cover(import_path_text_range); + } else { + builder.delete(import_path_text_range); + } + } + builder.replace(module.text_range, module_def) } }, ) } +fn generate_module_def( + parent_impl: &Option, + module: &mut Module, + old_indent: IndentLevel, +) -> String { + let (items_to_be_processed, new_item_indent) = if parent_impl.is_some() { + (Either::Left(module.body_items.iter()), old_indent + 2) + } else { + (Either::Right(module.use_items.iter().chain(module.body_items.iter())), old_indent + 1) + }; + + let mut body = items_to_be_processed + .map(|item| item.indent(IndentLevel(1))) + .map(|item| format!("{new_item_indent}{item}")) + .join("\n\n"); + + if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { + let impl_indent = old_indent + 1; + body = format!("{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}"); + + // Add the import for enum/struct corresponding to given impl block + module.make_use_stmt_of_node_with_super(self_ty.syntax()); + for item in module.use_items.iter() { + body = format!("{impl_indent}{item}\n\n{body}"); + } + } + + let module_name = module.name; + format!("mod {module_name} {{\n{body}\n{old_indent}}}") +} + #[derive(Debug)] struct Module { text_range: TextRange, @@ -233,20 +231,24 @@ impl Module { fn get_usages_and_record_fields( &self, ctx: &AssistContext<'_>, - ) -> (FxHashMap>, Vec) { + ) -> (FxHashMap>, Vec, FxHashMap) + { let mut adt_fields = Vec::new(); let mut refs: FxHashMap> = FxHashMap::default(); + // use `TextSize` as key to avoid repeated use stmts + let mut use_stmts_to_be_inserted = FxHashMap::default(); //Here impl is not included as each item inside impl will be tied to the parent of //implementing block(a struct, enum, etc), if the parent is in selected module, it will //get updated by ADT section given below or if it is not, then we dont need to do any operation + for item in &self.body_items { match_ast! { match (item.syntax()) { ast::Adt(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Adt(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); //Enum Fields are not allowed to explicitly specify pub, it is implied match it { @@ -280,30 +282,30 @@ impl Module { ast::TypeAlias(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::TypeAlias(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Const(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Const(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Static(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Static(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Fn(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Function(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Macro(it) => { if let Some(nod) = ctx.sema.to_def(&it) { - self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs); + self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); } }, _ => (), @@ -311,7 +313,7 @@ impl Module { } } - (refs, adt_fields) + (refs, adt_fields, use_stmts_to_be_inserted) } fn expand_and_group_usages_file_wise( @@ -319,49 +321,62 @@ impl Module { ctx: &AssistContext<'_>, node_def: Definition, refs_in_files: &mut FxHashMap>, + use_stmts_to_be_inserted: &mut FxHashMap, ) { - for (file_id, references) in node_def.usages(&ctx.sema).all() { + let mod_name = self.name; + let covering_node = match ctx.covering_element() { + syntax::NodeOrToken::Node(node) => node, + syntax::NodeOrToken::Token(tok) => tok.parent().unwrap(), // won't panic + }; + let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range()); + let mut use_stmts_set = FxHashSet::default(); + + for (file_id, refs) in node_def.usages(&ctx.sema).all() { let source_file = ctx.sema.parse(file_id); - let usages_in_file = references - .into_iter() - .filter_map(|usage| self.get_usage_to_be_processed(&source_file, usage)); - refs_in_files.entry(file_id).or_default().extend(usages_in_file); - } - } + let usages = refs.into_iter().filter_map(|FileReference { range, .. }| { + // handle normal usages + let name_ref = find_node_at_range::(source_file.syntax(), range)?; - fn get_usage_to_be_processed( - &self, - source_file: &SourceFile, - FileReference { range, name, .. }: FileReference, - ) -> Option<(TextRange, String)> { - let path: ast::Path = find_node_at_range(source_file.syntax(), range)?; - - for desc in path.syntax().descendants() { - if desc.to_string() == name.syntax().to_string() - && !self.text_range.contains_range(desc.text_range()) - { - if let Some(name_ref) = ast::NameRef::cast(desc) { - let mod_name = self.name; - return Some(( - name_ref.syntax().text_range(), - format!("{mod_name}::{name_ref}"), - )); + if out_of_sel(name_ref.syntax()) { + let new_ref = format!("{mod_name}::{name_ref}"); + return Some((range, new_ref)); + } else if let Some(use_) = name_ref.syntax().ancestors().find_map(ast::Use::cast) { + // handle usages in use_stmts which is in_sel + // check if `use` is top stmt in selection + if use_.syntax().parent().is_some_and(|parent| parent == covering_node) + && use_stmts_set.insert(use_.syntax().text_range().start()) + { + let use_ = use_stmts_to_be_inserted + .entry(use_.syntax().text_range().start()) + .or_insert_with(|| use_.clone_subtree().clone_for_update()); + for seg in use_ + .syntax() + .descendants() + .filter_map(ast::NameRef::cast) + .filter(|seg| seg.syntax().to_string() == name_ref.to_string()) + { + let new_ref = make::path_from_text(&format!("{mod_name}::{seg}")) + .clone_for_update(); + ted::replace(seg.syntax().parent()?, new_ref.syntax()); + } + } } - } - } - None + None + }); + refs_in_files.entry(file_id).or_default().extend(usages); + } } fn change_visibility(&mut self, record_fields: Vec) { let (mut replacements, record_field_parents, impls) = get_replacements_for_visibility_change(&mut self.body_items, false); - let mut impl_items: Vec = impls + let mut impl_items = impls .into_iter() .flat_map(|impl_| impl_.syntax().descendants()) .filter_map(ast::Item::cast) - .collect(); + .collect_vec(); let (mut impl_item_replacements, _, _) = get_replacements_for_visibility_change(&mut impl_items, true); @@ -394,133 +409,88 @@ impl Module { fn resolve_imports( &mut self, - curr_parent_module: Option, + module: Option, ctx: &AssistContext<'_>, ) -> Vec { - let mut import_paths_to_be_removed: Vec = vec![]; - let mut node_set: FxHashSet = FxHashSet::default(); + let mut imports_to_remove = vec![]; + let mut node_set = FxHashSet::default(); for item in self.body_items.clone() { - for x in item.syntax().descendants() { - if let Some(name) = ast::Name::cast(x.clone()) { - if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) { - //Necessary to avoid two same names going through - if !node_set.contains(&name.syntax().to_string()) { - node_set.insert(name.syntax().to_string()); - let def_opt: Option = match name_classify { - NameClass::Definition(def) => Some(def), - _ => None, - }; - - if let Some(def) = def_opt { - if let Some(import_path) = self - .process_names_and_namerefs_for_import_resolve( - def, - name.syntax(), - &curr_parent_module, - ctx, - ) - { - check_intersection_and_push( - &mut import_paths_to_be_removed, - import_path, - ); - } - } + item.syntax() + .descendants() + .filter_map(|x| { + if let Some(name) = ast::Name::cast(x.clone()) { + NameClass::classify(&ctx.sema, &name).and_then(|nc| match nc { + NameClass::Definition(def) => Some((name.syntax().clone(), def)), + _ => None, + }) + } else if let Some(name_ref) = ast::NameRef::cast(x) { + NameRefClass::classify(&ctx.sema, &name_ref).and_then(|nc| match nc { + NameRefClass::Definition(def) => Some((name_ref.syntax().clone(), def)), + _ => None, + }) + } else { + None + } + }) + .for_each(|(node, def)| { + if node_set.insert(node.to_string()) { + if let Some(import) = self.process_def_in_sel(def, &node, &module, ctx) { + check_intersection_and_push(&mut imports_to_remove, import); } } - } - - if let Some(name_ref) = ast::NameRef::cast(x) { - if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) { - //Necessary to avoid two same names going through - if !node_set.contains(&name_ref.syntax().to_string()) { - node_set.insert(name_ref.syntax().to_string()); - let def_opt: Option = match name_classify { - NameRefClass::Definition(def) => Some(def), - _ => None, - }; - - if let Some(def) = def_opt { - if let Some(import_path) = self - .process_names_and_namerefs_for_import_resolve( - def, - name_ref.syntax(), - &curr_parent_module, - ctx, - ) - { - check_intersection_and_push( - &mut import_paths_to_be_removed, - import_path, - ); - } - } - } - } - } - } + }) } - import_paths_to_be_removed + imports_to_remove } - fn process_names_and_namerefs_for_import_resolve( + fn process_def_in_sel( &mut self, def: Definition, - node_syntax: &SyntaxNode, + use_node: &SyntaxNode, curr_parent_module: &Option, ctx: &AssistContext<'_>, ) -> Option { //We only need to find in the current file let selection_range = ctx.selection_trimmed(); - let curr_file_id = ctx.file_id(); - let search_scope = SearchScope::single_file(curr_file_id); - let usage_res = def.usages(&ctx.sema).in_scope(&search_scope).all(); - let file = ctx.sema.parse(curr_file_id); + let file_id = ctx.file_id(); + let usage_res = def.usages(&ctx.sema).in_scope(&SearchScope::single_file(file_id)).all(); + let file = ctx.sema.parse(file_id); - let mut exists_inside_sel = false; - let mut exists_outside_sel = false; - for (_, refs) in usage_res.iter() { - let mut non_use_nodes_itr = refs.iter().filter_map(|x| { - if find_node_at_range::(file.syntax(), x.range).is_none() { - let path_opt = find_node_at_range::(file.syntax(), x.range); - return path_opt; - } - - None - }); - - if non_use_nodes_itr - .clone() - .any(|x| !selection_range.contains_range(x.syntax().text_range())) + // track uses which does not exists in `Use` + let mut uses_exist_in_sel = false; + let mut uses_exist_out_sel = false; + 'outside: for (_, refs) in usage_res.iter() { + for x in refs + .iter() + .filter(|x| find_node_at_range::(file.syntax(), x.range).is_none()) + .filter_map(|x| find_node_at_range::(file.syntax(), x.range)) { - exists_outside_sel = true; - } - if non_use_nodes_itr.any(|x| selection_range.contains_range(x.syntax().text_range())) { - exists_inside_sel = true; + let in_selection = selection_range.contains_range(x.syntax().text_range()); + uses_exist_in_sel |= in_selection; + uses_exist_out_sel |= !in_selection; + + if uses_exist_in_sel && uses_exist_out_sel { + break 'outside; + } } } - let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod( - def, - ctx, - curr_parent_module, - selection_range, - curr_file_id, - ); + let (def_in_mod, def_out_sel) = + check_def_in_mod_and_out_sel(def, ctx, curr_parent_module, selection_range, file_id); - let use_stmt_opt: Option = usage_res.into_iter().find_map(|(file_id, refs)| { - if file_id == curr_file_id { - refs.into_iter() - .rev() - .find_map(|fref| find_node_at_range(file.syntax(), fref.range)) - } else { - None - } + // Find use stmt that use def in current file + let use_stmt: Option = usage_res + .into_iter() + .filter(|(use_file_id, _)| *use_file_id == file_id) + .flat_map(|(_, refs)| refs.into_iter().rev()) + .find_map(|fref| find_node_at_range(file.syntax(), fref.range)); + let use_stmt_not_in_sel = use_stmt.as_ref().is_some_and(|use_stmt| { + !selection_range.contains_range(use_stmt.syntax().text_range()) }); - let mut use_tree_str_opt: Option> = None; + let mut use_tree_paths: Option> = None; //Exists inside and outside selection // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new // module @@ -534,37 +504,37 @@ impl Module { //get the use_tree_str, reconstruct the use stmt in new module let mut import_path_to_be_removed: Option = None; - if exists_inside_sel && exists_outside_sel { + if uses_exist_in_sel && uses_exist_out_sel { //Changes to be made only inside new module //If use_stmt exists, find the use_tree_str, reconstruct it inside new module //If not, insert a use stmt with super and the given nameref - if let Some((use_tree_str, _)) = - self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax) - { - use_tree_str_opt = Some(use_tree_str); - } else if source_exists_outside_sel_in_same_mod { - //Considered only after use_stmt is not present - //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel = - //true for all cases) - // false | false -> Do nothing - // false | true -> If source is in selection -> nothing to do, If source is outside - // mod -> ust_stmt transversal - // true | false -> super import insertion - // true | true -> super import insertion - self.make_use_stmt_of_node_with_super(node_syntax); + match self.process_use_stmt_for_import_resolve(use_stmt, use_node) { + Some((use_tree_str, _)) => use_tree_paths = Some(use_tree_str), + None if def_in_mod && def_out_sel => { + //Considered only after use_stmt is not present + //def_in_mod && def_out_sel | exists_outside_sel(exists_inside_sel = + //true for all cases) + // false | false -> Do nothing + // false | true -> If source is in selection -> nothing to do, If source is outside + // mod -> ust_stmt transversal + // true | false -> super import insertion + // true | true -> super import insertion + self.make_use_stmt_of_node_with_super(use_node); + } + None => {} } - } else if exists_inside_sel && !exists_outside_sel { + } else if uses_exist_in_sel && !uses_exist_out_sel { //Changes to be made inside new module, and remove import from outside if let Some((mut use_tree_str, text_range_opt)) = - self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax) + self.process_use_stmt_for_import_resolve(use_stmt, use_node) { if let Some(text_range) = text_range_opt { import_path_to_be_removed = Some(text_range); } - if source_exists_outside_sel_in_same_mod { + if def_in_mod && def_out_sel { if let Some(first_path_in_use_tree) = use_tree_str.last() { let first_path_in_use_tree_str = first_path_in_use_tree.to_string(); if !first_path_in_use_tree_str.contains("super") @@ -576,31 +546,43 @@ impl Module { } } - use_tree_str_opt = Some(use_tree_str); - } else if source_exists_outside_sel_in_same_mod { - self.make_use_stmt_of_node_with_super(node_syntax); + use_tree_paths = Some(use_tree_str); + } else if def_in_mod && def_out_sel { + self.make_use_stmt_of_node_with_super(use_node); } } - if let Some(use_tree_str) = use_tree_str_opt { - let mut use_tree_str = use_tree_str; - use_tree_str.reverse(); + if let Some(mut use_tree_paths) = use_tree_paths { + use_tree_paths.reverse(); - if !(!exists_outside_sel && exists_inside_sel && source_exists_outside_sel_in_same_mod) - { - if let Some(first_path_in_use_tree) = use_tree_str.first() { - let first_path_in_use_tree_str = first_path_in_use_tree.to_string(); - if first_path_in_use_tree_str.contains("super") { - let super_path = make::ext::ident_path("super"); - use_tree_str.insert(0, super_path) + if uses_exist_out_sel || !uses_exist_in_sel || !def_in_mod || !def_out_sel { + if let Some(first_path_in_use_tree) = use_tree_paths.first() { + if first_path_in_use_tree.to_string().contains("super") { + use_tree_paths.insert(0, make::ext::ident_path("super")); } } } - let use_ = - make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false)); - let item = ast::Item::from(use_); - self.use_items.insert(0, item); + let is_item = matches!( + def, + Definition::Macro(_) + | Definition::Module(_) + | Definition::Function(_) + | Definition::Adt(_) + | Definition::Const(_) + | Definition::Static(_) + | Definition::Trait(_) + | Definition::TraitAlias(_) + | Definition::TypeAlias(_) + ); + + if (def_out_sel || !is_item) && use_stmt_not_in_sel { + let use_ = make::use_( + None, + make::use_tree(make::join_paths(use_tree_paths), None, None, false), + ); + self.use_items.insert(0, ast::Item::from(use_)); + } } import_path_to_be_removed @@ -621,33 +603,26 @@ impl Module { fn process_use_stmt_for_import_resolve( &self, - use_stmt_opt: Option, + use_stmt: Option, node_syntax: &SyntaxNode, ) -> Option<(Vec, Option)> { - if let Some(use_stmt) = use_stmt_opt { - for desc in use_stmt.syntax().descendants() { - if let Some(path_seg) = ast::PathSegment::cast(desc) { - if path_seg.syntax().to_string() == node_syntax.to_string() { - let mut use_tree_str = vec![path_seg.parent_path()]; - get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str); - for ancs in path_seg.syntax().ancestors() { - //Here we are looking for use_tree with same string value as node - //passed above as the range_to_remove function looks for a comma and - //then includes it in the text range to remove it. But the comma only - //appears at the use_tree level - if let Some(use_tree) = ast::UseTree::cast(ancs) { - if use_tree.syntax().to_string() == node_syntax.to_string() { - return Some(( - use_tree_str, - Some(range_to_remove(use_tree.syntax())), - )); - } - } - } + let use_stmt = use_stmt?; + for path_seg in use_stmt.syntax().descendants().filter_map(ast::PathSegment::cast) { + if path_seg.syntax().to_string() == node_syntax.to_string() { + let mut use_tree_str = vec![path_seg.parent_path()]; + get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str); - return Some((use_tree_str, None)); + //Here we are looking for use_tree with same string value as node + //passed above as the range_to_remove function looks for a comma and + //then includes it in the text range to remove it. But the comma only + //appears at the use_tree level + for use_tree in path_seg.syntax().ancestors().filter_map(ast::UseTree::cast) { + if use_tree.syntax().to_string() == node_syntax.to_string() { + return Some((use_tree_str, Some(range_to_remove(use_tree.syntax())))); } } + + return Some((use_tree_str, None)); } } @@ -676,145 +651,58 @@ fn check_intersection_and_push( import_paths_to_be_removed.push(import_path); } -fn does_source_exists_outside_sel_in_same_mod( +fn check_def_in_mod_and_out_sel( def: Definition, ctx: &AssistContext<'_>, curr_parent_module: &Option, selection_range: TextRange, curr_file_id: FileId, -) -> bool { - let mut source_exists_outside_sel_in_same_mod = false; +) -> (bool, bool) { + macro_rules! check_item { + ($x:ident) => { + if let Some(source) = $x.source(ctx.db()) { + let have_same_parent = if let Some(ast_module) = &curr_parent_module { + ctx.sema.to_module_def(ast_module).is_some_and(|it| it == $x.module(ctx.db())) + } else { + source.file_id.original_file(ctx.db()) == curr_file_id + }; + + let in_sel = !selection_range.contains_range(source.value.syntax().text_range()); + return (have_same_parent, in_sel); + } + }; + } + match def { Definition::Module(x) => { let source = x.definition_source(ctx.db()); - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - if let Some(hir_module) = x.parent(ctx.db()) { - compare_hir_and_ast_module(ast_module, hir_module, ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id + let have_same_parent = match (&curr_parent_module, x.parent(ctx.db())) { + (Some(ast_module), Some(hir_module)) => { + ctx.sema.to_module_def(ast_module).is_some_and(|it| it == hir_module) } - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id + _ => source.file_id.original_file(ctx.db()) == curr_file_id, }; if have_same_parent { if let ModuleSource::Module(module_) = source.value { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(module_.syntax().text_range()); + let in_sel = !selection_range.contains_range(module_.syntax().text_range()); + return (have_same_parent, in_sel); } } - } - Definition::Function(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Adt(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Variant(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Const(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Static(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Trait(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::TypeAlias(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } + return (have_same_parent, false); } + Definition::Function(x) => check_item!(x), + Definition::Adt(x) => check_item!(x), + Definition::Variant(x) => check_item!(x), + Definition::Const(x) => check_item!(x), + Definition::Static(x) => check_item!(x), + Definition::Trait(x) => check_item!(x), + Definition::TypeAlias(x) => check_item!(x), _ => {} } - source_exists_outside_sel_in_same_mod + (false, false) } fn get_replacements_for_visibility_change( @@ -834,24 +722,30 @@ fn get_replacements_for_visibility_change( *item = item.clone_for_update(); } //Use stmts are ignored + macro_rules! push_to_replacement { + ($it:ident) => { + replacements.push(($it.visibility(), $it.syntax().clone())) + }; + } + match item { - ast::Item::Const(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::Enum(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::ExternCrate(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::Fn(it) => replacements.push((it.visibility(), it.syntax().clone())), + ast::Item::Const(it) => push_to_replacement!(it), + ast::Item::Enum(it) => push_to_replacement!(it), + ast::Item::ExternCrate(it) => push_to_replacement!(it), + ast::Item::Fn(it) => push_to_replacement!(it), //Associated item's visibility should not be changed ast::Item::Impl(it) if it.for_token().is_none() => impls.push(it.clone()), - ast::Item::MacroDef(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::Module(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::Static(it) => replacements.push((it.visibility(), it.syntax().clone())), + ast::Item::MacroDef(it) => push_to_replacement!(it), + ast::Item::Module(it) => push_to_replacement!(it), + ast::Item::Static(it) => push_to_replacement!(it), ast::Item::Struct(it) => { - replacements.push((it.visibility(), it.syntax().clone())); + push_to_replacement!(it); record_field_parents.push((it.visibility(), it.syntax().clone())); } - ast::Item::Trait(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::TypeAlias(it) => replacements.push((it.visibility(), it.syntax().clone())), + ast::Item::Trait(it) => push_to_replacement!(it), + ast::Item::TypeAlias(it) => push_to_replacement!(it), ast::Item::Union(it) => { - replacements.push((it.visibility(), it.syntax().clone())); + push_to_replacement!(it); record_field_parents.push((it.visibility(), it.syntax().clone())); } _ => (), @@ -865,8 +759,11 @@ fn get_use_tree_paths_from_path( path: ast::Path, use_tree_str: &mut Vec, ) -> Option<&mut Vec> { - path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| { - if let Some(use_tree) = ast::UseTree::cast(x) { + path.syntax() + .ancestors() + .filter(|x| x.to_string() != path.to_string()) + .filter_map(ast::UseTree::cast) + .find_map(|use_tree| { if let Some(upper_tree_path) = use_tree.path() { if upper_tree_path.to_string() != path.to_string() { use_tree_str.push(upper_tree_path.clone()); @@ -874,9 +771,8 @@ fn get_use_tree_paths_from_path( return Some(use_tree); } } - } - None - })?; + None + })?; Some(use_tree_str) } @@ -890,20 +786,6 @@ fn add_change_vis(vis: Option, node_or_token_opt: Option, -) -> Option<()> { - let hir_mod_name = hir_module.name(ctx.db())?; - let ast_mod_name = ast_module.name()?; - if hir_mod_name.display(ctx.db()).to_string() != ast_mod_name.to_string() { - return None; - } - - Some(()) -} - fn indent_range_before_given_node(node: &SyntaxNode) -> Option { node.siblings_with_tokens(syntax::Direction::Prev) .find(|x| x.kind() == WHITESPACE) @@ -1799,6 +1681,54 @@ mod modname { pub(crate) condvar: B, } } +"#, + ); + } + + #[test] + fn test_remove_import_path_inside_selection() { + check_assist( + extract_module, + r#" +$0struct Point; +impl Point { + pub const fn direction(self, other: Self) -> Option { + Some(Vertical) + } +} + +pub enum Direction { + Horizontal, + Vertical, +} +use Direction::{Horizontal, Vertical};$0 + +fn main() { + let x = Vertical; +} +"#, + r#" +mod modname { + use Direction::{Horizontal, Vertical}; + + pub(crate) struct Point; + + impl Point { + pub const fn direction(self, other: Self) -> Option { + Some(Vertical) + } + } + + pub enum Direction { + Horizontal, + Vertical, + } +} +use modname::Direction::{Horizontal, Vertical}; + +fn main() { + let x = Vertical; +} "#, ); } diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index fe2f8ed6417d..ff051fa870f5 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -198,7 +198,7 @@ fn get_adt_source( adt: &hir::Adt, fn_name: &str, ) -> Option<(Option, FileId)> { - let range = adt.source(ctx.sema.db)?.syntax().original_file_range(ctx.sema.db); + let range = adt.source(ctx.sema.db)?.syntax().original_file_range_rooted(ctx.sema.db); let file = ctx.sema.parse(range.file_id); let adt_source = ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?; diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 50ec4347dc2a..a90fe83857e9 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -206,7 +206,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let fn_body = fn_source.value.body()?; let param_list = fn_source.value.param_list()?; - let FileRange { file_id, range } = fn_source.syntax().original_file_range(ctx.sema.db); + let FileRange { file_id, range } = fn_source.syntax().original_file_range_rooted(ctx.sema.db); if file_id == ctx.file_id() && range.contains(ctx.offset()) { cov_mark::hit!(inline_call_recursive); return None; diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 35e6b97eb783..4005753773c0 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -1,7 +1,10 @@ //! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) -use hir::Semantics; -use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase}; -use syntax::ast::{self, IsString}; +use hir::MacroFileIdExt; +use ide_db::syntax_helpers::node_ext::macro_call_for_string_token; +use syntax::{ + ast::{self, IsString}, + AstToken, +}; use crate::{ completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind, @@ -32,10 +35,24 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ pub(crate) fn complete_cargo_env_vars( acc: &mut Completions, ctx: &CompletionContext<'_>, + original: &ast::String, expanded: &ast::String, ) -> Option<()> { - guard_env_macro(expanded, &ctx.sema)?; - let range = expanded.text_range_between_quotes()?; + let is_in_env_expansion = ctx + .sema + .hir_file_for(&expanded.syntax().parent()?) + .macro_file() + .map_or(false, |it| it.is_env_or_option_env(ctx.sema.db)); + if !is_in_env_expansion { + let call = macro_call_for_string_token(expanded)?; + let makro = ctx.sema.resolve_macro_call(&call)?; + // We won't map into `option_env` as that generates `None` for non-existent env vars + // so fall back to this lookup + if !makro.is_env_or_option_env(ctx.sema.db) { + return None; + } + } + let range = original.text_range_between_quotes()?; CARGO_DEFINED_VARS.iter().for_each(|&(var, detail)| { let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var); @@ -46,18 +63,6 @@ pub(crate) fn complete_cargo_env_vars( Some(()) } -fn guard_env_macro(string: &ast::String, semantics: &Semantics<'_, RootDatabase>) -> Option<()> { - let call = macro_call_for_string_token(string)?; - let name = call.path()?.segment()?.name_ref()?; - let makro = semantics.resolve_macro_call(&call)?; - let db = semantics.db; - - match name.text().as_str() { - "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()), - _ => None, - } -} - #[cfg(test)] mod tests { use crate::tests::{check_edit, completion_list}; @@ -68,7 +73,7 @@ mod tests { &format!( r#" #[rustc_builtin_macro] - macro_rules! {macro_name} {{ + macro {macro_name} {{ ($var:literal) => {{ 0 }} }} @@ -80,7 +85,7 @@ mod tests { &format!( r#" #[rustc_builtin_macro] - macro_rules! {macro_name} {{ + macro {macro_name} {{ ($var:literal) => {{ 0 }} }} diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 7394d63be586..794678415022 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -96,7 +96,7 @@ fn complete_trait_impl_name( .parent() } }?; - let item = ctx.sema.original_syntax_node(&item)?; + let item = ctx.sema.original_syntax_node_rooted(&item)?; // item -> ASSOC_ITEM_LIST -> IMPL let impl_def = ast::Impl::cast(item.parent()?.parent()?)?; let replacement_range = { diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index ecf5b29e2c0c..c2faa2d939d8 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs @@ -2,7 +2,7 @@ use std::iter; -use hir::{HirFileIdExt, Module, ModuleSource}; +use hir::{HirFileIdExt, Module}; use ide_db::{ base_db::{SourceDatabaseExt, VfsPath}, FxHashSet, RootDatabase, SymbolKind, @@ -57,7 +57,7 @@ pub(crate) fn complete_mod( .collect::>(); let module_declaration_file = - current_module.declaration_source(ctx.db).map(|module_declaration_source_file| { + current_module.declaration_source_range(ctx.db).map(|module_declaration_source_file| { module_declaration_source_file.file_id.original_file(ctx.db) }); @@ -148,9 +148,7 @@ fn module_chain_to_containing_module_file( ) -> Vec { let mut path = iter::successors(Some(current_module), |current_module| current_module.parent(db)) - .take_while(|current_module| { - matches!(current_module.definition_source(db).value, ModuleSource::Module(_)) - }) + .take_while(|current_module| current_module.is_inline(db)) .collect::>(); path.reverse(); path diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 4bab2886851a..357060817c7b 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -369,6 +369,7 @@ impl CompletionItemKind { SymbolKind::LifetimeParam => "lt", SymbolKind::Local => "lc", SymbolKind::Macro => "ma", + SymbolKind::ProcMacro => "pm", SymbolKind::Module => "md", SymbolKind::SelfParam => "sp", SymbolKind::SelfType => "sy", diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 912f2fba2b3d..d89cfc8b6cb8 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -207,7 +207,7 @@ pub fn completions( CompletionAnalysis::String { original, expanded: Some(expanded) } => { completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); - completions::env_vars::complete_cargo_env_vars(acc, ctx, expanded); + completions::env_vars::complete_cargo_env_vars(acc, ctx, original, expanded); } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index 017635d88e74..ec05f6d13d11 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -205,6 +205,7 @@ impl RootDatabase { // SourceDatabaseExt base_db::FileTextQuery + base_db::CompressedFileTextQuery base_db::FileSourceRootQuery base_db::SourceRootQuery base_db::SourceRootCratesQuery diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 33970de1e4bd..c0f0faba35cd 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -407,7 +407,7 @@ impl NameClass { } pub fn classify(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "classify_name").entered(); + let _p = tracing::span!(tracing::Level::INFO, "NameClass::classify").entered(); let parent = name.syntax().parent()?; @@ -499,7 +499,8 @@ impl NameClass { sema: &Semantics<'_, RootDatabase>, lifetime: &ast::Lifetime, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "classify_lifetime", ?lifetime).entered(); + let _p = tracing::span!(tracing::Level::INFO, "NameClass::classify_lifetime", ?lifetime) + .entered(); let parent = lifetime.syntax().parent()?; if let Some(it) = ast::LifetimeParam::cast(parent.clone()) { @@ -590,7 +591,8 @@ impl NameRefClass { sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameRef, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "classify_name_ref", ?name_ref).entered(); + let _p = + tracing::span!(tracing::Level::INFO, "NameRefClass::classify", ?name_ref).entered(); let parent = name_ref.syntax().parent()?; @@ -689,7 +691,8 @@ impl NameRefClass { sema: &Semantics<'_, RootDatabase>, lifetime: &ast::Lifetime, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "classify_lifetime_ref", ?lifetime).entered(); + let _p = tracing::span!(tracing::Level::INFO, "NameRefClass::classify_lifetime", ?lifetime) + .entered(); let parent = lifetime.syntax().parent()?; match parent.kind() { SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index 4ac8a7c4c4aa..db44b1e72325 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -71,7 +71,7 @@ pub fn visit_file_defs( let mut defs: VecDeque<_> = module.declarations(db).into(); while let Some(def) = defs.pop_front() { if let ModuleDef::Module(submodule) = def { - if let hir::ModuleSource::Module(_) = submodule.definition_source(db).value { + if submodule.is_inline(db) { defs.extend(submodule.declarations(db)); submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); } diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index be08b37bac34..0d5a93f7b8e5 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -51,6 +51,7 @@ use std::{fmt, mem::ManuallyDrop}; use base_db::{ salsa::{self, Durability}, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, + DEFAULT_FILE_TEXT_LRU_CAP, }; use hir::db::{DefDatabase, ExpandDatabase, HirDatabase}; use triomphe::Arc; @@ -157,6 +158,7 @@ impl RootDatabase { pub fn update_base_query_lru_capacities(&mut self, lru_capacity: Option) { let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP); + base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP); base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); // macro expansions are usually rather small, so we can afford to keep more of them alive hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity); @@ -166,6 +168,7 @@ impl RootDatabase { pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap, usize>) { use hir::db as hir_db; + base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP); base_db::ParseQuery.in_db_mut(self).set_lru_capacity( lru_capacities .get(stringify!(ParseQuery)) @@ -199,7 +202,7 @@ impl RootDatabase { // base_db::ProcMacrosQuery // SourceDatabaseExt - // base_db::FileTextQuery + base_db::FileTextQuery // base_db::FileSourceRootQuery // base_db::SourceRootQuery base_db::SourceRootCratesQuery @@ -348,6 +351,7 @@ pub enum SymbolKind { LifetimeParam, Local, Macro, + ProcMacro, Module, SelfParam, SelfType, @@ -366,9 +370,8 @@ pub enum SymbolKind { impl From for SymbolKind { fn from(it: hir::MacroKind) -> Self { match it { - hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::ProcMacro => { - SymbolKind::Macro - } + hir::MacroKind::Declarative | hir::MacroKind::BuiltIn => SymbolKind::Macro, + hir::MacroKind::ProcMacro => SymbolKind::ProcMacro, hir::MacroKind::Derive => SymbolKind::Derive, hir::MacroKind::Attr => SymbolKind::Attribute, } @@ -381,6 +384,7 @@ impl From for SymbolKind { hir::ModuleDefId::ConstId(..) => SymbolKind::Const, hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant, hir::ModuleDefId::FunctionId(..) => SymbolKind::Function, + hir::ModuleDefId::MacroId(hir::MacroId::ProcMacroId(..)) => SymbolKind::ProcMacro, hir::ModuleDefId::MacroId(..) => SymbolKind::Macro, hir::ModuleDefId::ModuleId(..) => SymbolKind::Module, hir::ModuleDefId::StaticId(..) => SymbolKind::Static, diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 006d8882c11e..a3ecc1036059 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -190,22 +190,15 @@ impl SearchScope { let mut entries = IntMap::default(); let (file_id, range) = { - let InFile { file_id, value } = module.definition_source(db); + let InFile { file_id, value } = module.definition_source_range(db); if let Some(InRealFile { file_id, value: call_source }) = file_id.original_call_node(db) { (file_id, Some(call_source.text_range())) } else { - ( - file_id.original_file(db), - match value { - ModuleSource::SourceFile(_) => None, - ModuleSource::Module(it) => Some(it.syntax().text_range()), - ModuleSource::BlockExpr(it) => Some(it.syntax().text_range()), - }, - ) + (file_id.original_file(db), Some(value)) } }; - entries.insert(file_id, range); + entries.entry(file_id).or_insert(range); let mut to_visit: Vec<_> = module.children(db).collect(); while let Some(module) = to_visit.pop() { diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index db28928a24ea..a0fad7c850c6 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -38,7 +38,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option, d: &hir::UnresolvedField) -> Option> { + let mut fixes = Vec::new(); if d.method_with_same_name_exists { - method_fix(ctx, &d.expr) - } else { - // FIXME: add quickfix - - None + fixes.extend(method_fix(ctx, &d.expr)); } + fixes.extend(field_fix(ctx, d)); + if fixes.is_empty() { + None + } else { + Some(fixes) + } +} + +// FIXME: Add Snippet Support +fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option { + // Get the FileRange of the invalid field access + let root = ctx.sema.db.parse_or_expand(d.expr.file_id); + let expr = d.expr.value.to_node(&root); + + let error_range = ctx.sema.original_range_opt(expr.syntax())?; + let field_name = d.name.as_str()?; + // Convert the receiver to an ADT + let adt = d.receiver.strip_references().as_adt()?; + let target_module = adt.module(ctx.sema.db); + + let suggested_type = + if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) { + let display = + new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok(); + make::ty(display.as_deref().unwrap_or("()")) + } else { + make::ty("()") + }; + + if !is_editable_crate(target_module.krate(), ctx.sema.db) { + return None; + } + + match adt { + Adt::Struct(adt_struct) => { + add_field_to_struct_fix(ctx, adt_struct, field_name, suggested_type, error_range) + } + Adt::Union(adt_union) => { + add_variant_to_union(ctx, adt_union, field_name, suggested_type, error_range) + } + _ => None, + } +} + +fn add_variant_to_union( + ctx: &DiagnosticsContext<'_>, + adt_union: Union, + field_name: &str, + suggested_type: Type, + error_range: FileRange, +) -> Option { + let adt_source = adt_union.source(ctx.sema.db)?; + let adt_syntax = adt_source.syntax(); + let field_list = adt_source.value.record_field_list()?; + let range = adt_syntax.original_file_range_rooted(ctx.sema.db); + let field_name = make::name(field_name); + + let (offset, record_field) = + record_field_layout(None, field_name, suggested_type, field_list, adt_syntax.value)?; + + let mut src_change_builder = SourceChangeBuilder::new(range.file_id); + src_change_builder.insert(offset, record_field); + Some(Assist { + id: AssistId("add-variant-to-union", AssistKind::QuickFix), + label: Label::new("Add field to union".to_owned()), + group: None, + target: error_range.range, + source_change: Some(src_change_builder.finish()), + trigger_signature_help: false, + }) +} + +fn add_field_to_struct_fix( + ctx: &DiagnosticsContext<'_>, + adt_struct: Struct, + field_name: &str, + suggested_type: Type, + error_range: FileRange, +) -> Option { + let struct_source = adt_struct.source(ctx.sema.db)?; + let struct_syntax = struct_source.syntax(); + let struct_range = struct_syntax.original_file_range_rooted(ctx.sema.db); + let field_list = struct_source.value.field_list(); + match field_list { + Some(FieldList::RecordFieldList(field_list)) => { + // Get range of final field in the struct + let visibility = if error_range.file_id == struct_range.file_id { + None + } else { + Some(make::visibility_pub_crate()) + }; + let field_name = make::name(field_name); + + let (offset, record_field) = record_field_layout( + visibility, + field_name, + suggested_type, + field_list, + struct_syntax.value, + )?; + + let mut src_change_builder = SourceChangeBuilder::new(struct_range.file_id); + + // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563 + src_change_builder.insert(offset, record_field); + Some(Assist { + id: AssistId("add-field-to-record-struct", AssistKind::QuickFix), + label: Label::new("Add field to Record Struct".to_owned()), + group: None, + target: error_range.range, + source_change: Some(src_change_builder.finish()), + trigger_signature_help: false, + }) + } + None => { + // Add a field list to the Unit Struct + let mut src_change_builder = SourceChangeBuilder::new(struct_range.file_id); + let field_name = make::name(field_name); + let visibility = if error_range.file_id == struct_range.file_id { + None + } else { + Some(make::visibility_pub_crate()) + }; + // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563 + let indent = IndentLevel::from_node(struct_syntax.value) + 1; + + let field = make::record_field(visibility, field_name, suggested_type).indent(indent); + let record_field_list = make::record_field_list(iter::once(field)); + // A Unit Struct with no `;` is invalid syntax. We should not suggest this fix. + let semi_colon = + algo::skip_trivia_token(struct_syntax.value.last_token()?, Direction::Prev)?; + if semi_colon.kind() != SyntaxKind::SEMICOLON { + return None; + } + src_change_builder.replace(semi_colon.text_range(), record_field_list.to_string()); + + Some(Assist { + id: AssistId("convert-unit-struct-to-record-struct", AssistKind::QuickFix), + label: Label::new("Convert Unit Struct to Record Struct and add field".to_owned()), + group: None, + target: error_range.range, + source_change: Some(src_change_builder.finish()), + trigger_signature_help: false, + }) + } + Some(FieldList::TupleFieldList(_tuple)) => { + // FIXME: Add support for Tuple Structs. Tuple Structs are not sent to this diagnostic + None + } + } +} + +/// Used to determine the layout of the record field in the struct. +fn record_field_layout( + visibility: Option, + name: Name, + suggested_type: Type, + field_list: ast::RecordFieldList, + struct_syntax: &SyntaxNode, +) -> Option<(TextSize, String)> { + let (offset, needs_comma, trailing_new_line, indent) = match field_list.fields().last() { + Some(record_field) => { + let syntax = algo::skip_trivia_token(field_list.r_curly_token()?, Direction::Prev)?; + + let last_field_syntax = record_field.syntax(); + let last_field_indent = IndentLevel::from_node(last_field_syntax); + ( + last_field_syntax.text_range().end(), + syntax.kind() != SyntaxKind::COMMA, + false, + last_field_indent, + ) + } + // Empty Struct. Add a field right before the closing brace + None => { + let indent = IndentLevel::from_node(struct_syntax) + 1; + let offset = field_list.r_curly_token()?.text_range().start(); + (offset, false, true, indent) + } + }; + let comma = if needs_comma { ",\n" } else { "" }; + let trailing_new_line = if trailing_new_line { "\n" } else { "" }; + let record_field = make::record_field(visibility, name, suggested_type); + + Some((offset, format!("{comma}{indent}{record_field}{trailing_new_line}"))) } // FIXME: We should fill out the call here, move the cursor and trigger signature help fn method_fix( ctx: &DiagnosticsContext<'_>, expr_ptr: &InFile>, -) -> Option> { +) -> Option { let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?; - Some(vec![Assist { + Some(Assist { id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix), label: Label::new("Use parentheses to call the method".to_owned()), group: None, @@ -73,13 +266,15 @@ fn method_fix( TextEdit::insert(range.end(), "()".to_owned()), )), trigger_signature_help: false, - }]) + }) } #[cfg(test)] mod tests { + use crate::{ tests::{ check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled, + check_fix, }, DiagnosticsConfig, }; @@ -168,4 +363,100 @@ fn foo() { config.disabled.insert("syntax-error".to_owned()); check_diagnostics_with_config(config, "fn foo() { (). }"); } + + #[test] + fn unresolved_field_fix_on_unit() { + check_fix( + r#" + struct Foo; + + fn foo() { + Foo.bar$0; + } + "#, + r#" + struct Foo{ bar: () } + + fn foo() { + Foo.bar; + } + "#, + ); + } + #[test] + fn unresolved_field_fix_on_empty() { + check_fix( + r#" + struct Foo{ + } + + fn foo() { + let foo = Foo{}; + foo.bar$0; + } + "#, + r#" + struct Foo{ + bar: () + } + + fn foo() { + let foo = Foo{}; + foo.bar; + } + "#, + ); + } + #[test] + fn unresolved_field_fix_on_struct() { + check_fix( + r#" + struct Foo{ + a: i32 + } + + fn foo() { + let foo = Foo{a: 0}; + foo.bar$0; + } + "#, + r#" + struct Foo{ + a: i32, + bar: () + } + + fn foo() { + let foo = Foo{a: 0}; + foo.bar; + } + "#, + ); + } + #[test] + fn unresolved_field_fix_on_union() { + check_fix( + r#" + union Foo{ + a: i32 + } + + fn foo() { + let foo = Foo{a: 0}; + foo.bar$0; + } + "#, + r#" + union Foo{ + a: i32, + bar: () + } + + fn foo() { + let foo = Foo{a: 0}; + foo.bar; + } + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs index 28ccf474b40b..a9e1d07d7c52 100644 --- a/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -1,3 +1,11 @@ +use ide_db::{ + assists::{Assist, AssistId, AssistKind}, + base_db::FileRange, + label::Label, + source_change::SourceChange, +}; +use text_edit::TextEdit; + use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unused-variables @@ -8,18 +16,38 @@ pub(crate) fn unused_variables( d: &hir::UnusedVariable, ) -> Diagnostic { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + let diagnostic_range = ctx.sema.diagnostics_display_range(ast); + let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string(); Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcLint("unused_variables"), "unused variable", ast, ) + .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro())) .experimental() } +fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option> { + if is_in_marco { + return None; + } + Some(vec![Assist { + id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix), + label: Label::new(format!("Rename unused {} to _{}", var_name, var_name)), + group: None, + target: diagnostic_range.range, + source_change: Some(SourceChange::from_text_edit( + diagnostic_range.file_id, + TextEdit::replace(diagnostic_range.range, format!("_{}", var_name)), + )), + trigger_signature_help: false, + }]) +} + #[cfg(test)] mod tests { - use crate::tests::check_diagnostics; + use crate::tests::{check_diagnostics, check_fix, check_no_fix}; #[test] fn unused_variables_simple() { @@ -29,23 +57,23 @@ mod tests { struct Foo { f1: i32, f2: i64 } fn f(kkk: i32) {} - //^^^ warn: unused variable + //^^^ 💡 warn: unused variable fn main() { let a = 2; - //^ warn: unused variable + //^ 💡 warn: unused variable let b = 5; // note: `unused variable` implies `unused mut`, so we should not emit both at the same time. let mut c = f(b); - //^^^^^ warn: unused variable + //^^^^^ 💡 warn: unused variable let (d, e) = (3, 5); - //^ warn: unused variable + //^ 💡 warn: unused variable let _ = e; let f1 = 2; let f2 = 5; let f = Foo { f1, f2 }; match f { Foo { f1, f2 } => { - //^^ warn: unused variable + //^^ 💡 warn: unused variable _ = f2; } } @@ -53,7 +81,7 @@ fn main() { if g {} let h: fn() -> i32 = || 2; let i = h(); - //^ warn: unused variable + //^ 💡 warn: unused variable } "#, ); @@ -67,11 +95,11 @@ struct S { } impl S { fn owned_self(self, u: i32) {} - //^ warn: unused variable + //^ 💡 warn: unused variable fn ref_self(&self, u: i32) {} - //^ warn: unused variable + //^ 💡 warn: unused variable fn ref_mut_self(&mut self, u: i32) {} - //^ warn: unused variable + //^ 💡 warn: unused variable fn owned_mut_self(mut self) {} //^^^^^^^^ 💡 warn: variable does not need to be mutable @@ -103,7 +131,78 @@ fn main() { #[deny(unused)] fn main2() { let x = 2; - //^ error: unused variable + //^ 💡 error: unused variable +} +"#, + ); + } + + #[test] + fn fix_unused_variable() { + check_fix( + r#" +fn main() { + let x$0 = 2; +} +"#, + r#" +fn main() { + let _x = 2; +} +"#, + ); + + check_fix( + r#" +fn main() { + let ($0d, _e) = (3, 5); +} +"#, + r#" +fn main() { + let (_d, _e) = (3, 5); +} +"#, + ); + + check_fix( + r#" +struct Foo { f1: i32, f2: i64 } +fn main() { + let f = Foo { f1: 0, f2: 0 }; + match f { + Foo { f1$0, f2 } => { + _ = f2; + } + } +} +"#, + r#" +struct Foo { f1: i32, f2: i64 } +fn main() { + let f = Foo { f1: 0, f2: 0 }; + match f { + Foo { _f1, f2 } => { + _ = f2; + } + } +} +"#, + ); + } + + #[test] + fn no_fix_for_marco() { + check_no_fix( + r#" +macro_rules! my_macro { + () => { + let x = 3; + }; +} + +fn main() { + $0my_macro!(); } "#, ); diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs index fb98e9568474..cfda1c692aea 100644 --- a/crates/ide-ssr/src/matching.rs +++ b/crates/ide-ssr/src/matching.rs @@ -125,9 +125,12 @@ impl<'db, 'sema> Matcher<'db, 'sema> { let match_state = Matcher { sema, restrict_range: *restrict_range, rule }; // First pass at matching, where we check that node types and idents match. match_state.attempt_match_node(&mut Phase::First, &rule.pattern.node, code)?; - match_state.validate_range(&sema.original_range(code))?; + let file_range = sema + .original_range_opt(code) + .ok_or(MatchFailed { reason: Some("def site definition".to_owned()) })?; + match_state.validate_range(&file_range)?; let mut the_match = Match { - range: sema.original_range(code), + range: file_range, matched_node: code.clone(), placeholder_values: FxHashMap::default(), ignored_comments: Vec::new(), @@ -175,7 +178,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> { self.check_constraint(constraint, code)?; } if let Phase::Second(matches_out) = phase { - let original_range = self.sema.original_range(code); + let original_range = self + .sema + .original_range_opt(code) + .ok_or(MatchFailed { reason: Some("def site definition".to_owned()) })?; // We validated the range for the node when we started the match, so the placeholder // probably can't fail range validation, but just to be safe... self.validate_range(&original_range)?; @@ -487,7 +493,13 @@ impl<'db, 'sema> Matcher<'db, 'sema> { match_out.placeholder_values.insert( placeholder.ident.clone(), PlaceholderMatch::from_range(FileRange { - file_id: self.sema.original_range(code).file_id, + file_id: self + .sema + .original_range_opt(code) + .ok_or(MatchFailed { + reason: Some("def site definition".to_owned()), + })? + .file_id, range: first_matched_token .text_range() .cover(last_matched_token.text_range()), diff --git a/crates/ide-ssr/src/search.rs b/crates/ide-ssr/src/search.rs index 8d2d796122a2..55a49da24240 100644 --- a/crates/ide-ssr/src/search.rs +++ b/crates/ide-ssr/src/search.rs @@ -190,12 +190,9 @@ impl MatchFinder<'_> { // When matching within a macro expansion, we only want to allow matches of // nodes that originated entirely from within the token tree of the macro call. // i.e. we don't want to match something that came from the macro itself. - self.slow_scan_node( - &expanded, - rule, - &Some(self.sema.original_range(tt.syntax())), - matches_out, - ); + if let Some(range) = self.sema.original_range_opt(tt.syntax()) { + self.slow_scan_node(&expanded, rule, &Some(range), matches_out); + } } } } @@ -227,7 +224,7 @@ impl MatchFinder<'_> { // There is no range restriction. return true; } - let node_range = self.sema.original_range(code); + let Some(node_range) = self.sema.original_range_opt(code) else { return false }; for range in &self.restrict_ranges { if range.file_id == node_range.file_id && range.range.contains_range(node_range.range) { return true; diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 1bda15255dcd..ddeeca5f7b3e 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,10 +1,10 @@ -use std::mem::discriminant; +use std::{iter, mem::discriminant}; use crate::{ doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget, RangeInfo, TryToNav, }; -use hir::{AsAssocItem, AssocItem, DescendPreference, ModuleDef, Semantics}; +use hir::{AsAssocItem, AssocItem, DescendPreference, MacroFileIdExt, ModuleDef, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileId, FileLoader}, defs::{Definition, IdentClass}, @@ -74,11 +74,13 @@ pub(crate) fn goto_definition( .filter_map(|token| { let parent = token.parent()?; - if let Some(tt) = ast::TokenTree::cast(parent.clone()) { - if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) { + if let Some(token) = ast::String::cast(token.clone()) { + if let Some(x) = try_lookup_include_path(sema, token, file_id) { return Some(vec![x]); } + } + if ast::TokenTree::can_cast(parent.kind()) { if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token) { return Some(vec![x]); } @@ -111,24 +113,17 @@ pub(crate) fn goto_definition( fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, - tt: ast::TokenTree, - token: SyntaxToken, + token: ast::String, file_id: FileId, ) -> Option { - let token = ast::String::cast(token)?; - let path = token.value()?.into_owned(); - let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; - if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") { + let file = sema.hir_file_for(&token.syntax().parent()?).macro_file()?; + if !iter::successors(Some(file), |file| file.parent(sema.db).macro_file()) + // Check that we are in the eager argument expansion of an include macro + .any(|file| file.is_include_like_macro(sema.db) && file.eager_arg(sema.db).is_none()) + { return None; } - - // Ignore non-built-in macros to account for shadowing - if let Some(it) = sema.resolve_macro_call(¯o_call) { - if !matches!(it.kind(sema.db), hir::MacroKind::BuiltIn) { - return None; - } - } + let path = token.value()?; let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; let size = sema.db.file_text(file_id).len().try_into().ok()?; @@ -1531,6 +1526,26 @@ fn main() { ); } + #[test] + fn goto_include_has_eager_input() { + check( + r#" +//- /main.rs +#[rustc_builtin_macro] +macro_rules! include_str {} +#[rustc_builtin_macro] +macro_rules! concat {} + +fn main() { + let str = include_str!(concat!("foo", ".tx$0t")); +} +//- /foo.txt +// empty +//^file +"#, + ); + } + #[test] fn goto_doc_include_str() { check( diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index d1d039534d51..63777d491050 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -510,7 +510,7 @@ fn render_notable_trait_comment( let mut needs_impl_header = true; for (trait_, assoc_types) in notable_traits { desc.push_str(if mem::take(&mut needs_impl_header) { - " // Implements notable traits: " + "// Implements notable traits: " } else { ", " }); @@ -661,7 +661,7 @@ fn closure_ty( if let Some(layout) = render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) { - format_to!(markup, "{layout}"); + format_to!(markup, " {layout}"); } if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) { push_new_def(hir::Trait::from(trait_).into()) @@ -730,7 +730,7 @@ fn render_memory_layout( let config = config?; let layout = layout().ok()?; - let mut label = String::from(" // "); + let mut label = String::from("// "); if let Some(render) = config.size { let size = match tag(&layout) { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index c3cd6513dc61..4451e31870f2 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -180,7 +180,7 @@ fn foo() { *local* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let local: i32 ``` "#]], @@ -471,7 +471,7 @@ fn main() { *iter* ```rust - // size = 8, align = 4 + // size = 8, align = 4 let mut iter: Iter>, impl Fn(&mut u32, &u32, &mut u32) -> Option, u32>> ``` "#]], @@ -713,7 +713,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - // size = 1, align = 1, offset = 6 + // size = 1, align = 1, offset = 6 field_a: u8 ``` "#]], @@ -739,7 +739,7 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 pub field_a: u32 ``` "#]], @@ -762,7 +762,7 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 pub field_a: u32 ``` "#]], @@ -787,7 +787,7 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 pub 0: u32 ``` "#]], @@ -808,7 +808,7 @@ fn foo(foo: Foo) { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 pub 0: u32 ``` "#]], @@ -819,7 +819,7 @@ fn foo(foo: Foo) { fn hover_tuple_struct() { check( r#" -struct Foo$0(pub u32) +struct Foo$0(pub u32) where u32: Copy; "#, expect![[r#" *Foo* @@ -829,8 +829,100 @@ struct Foo$0(pub u32) ``` ```rust - // size = 4, align = 4 - struct Foo(pub u32); + // size = 4, align = 4 + struct Foo(pub u32) + where + u32: Copy, + ``` + "#]], + ); +} + +#[test] +fn hover_record_struct() { + check( + r#" +struct Foo$0 { field: u32 } +"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + struct Foo { + field: u32, + } + ``` + "#]], + ); + check( + r#" +struct Foo$0 where u32: Copy { field: u32 } +"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + struct Foo + where + u32: Copy, + { + field: u32, + } + ``` + "#]], + ); +} + +#[test] +fn hover_unit_struct() { + check( + r#" +struct Foo$0 where u32: Copy; +"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 0, align = 1 + struct Foo + where + u32: Copy, + ``` + "#]], + ); +} + +#[test] +fn hover_type_alias() { + check( + r#" +type Fo$0o: Trait = S where T: Trait; +"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + type Foo: Trait = S + where + T: Trait, ``` "#]], ); @@ -957,7 +1049,7 @@ fn main() { *zz* ```rust - // size = 8, align = 4 + // size = 8, align = 4 let zz: Test ``` "#]], @@ -1009,7 +1101,7 @@ fn main() { let b$0ar = Some(12); } *bar* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let bar: Option ``` "#]], @@ -1079,7 +1171,7 @@ fn hover_for_local_variable() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 foo: i32 ``` "#]], @@ -1094,7 +1186,7 @@ fn hover_for_local_variable_pat() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 foo: i32 ``` "#]], @@ -1109,7 +1201,7 @@ fn hover_local_var_edge() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 foo: i32 ``` "#]], @@ -1124,7 +1216,7 @@ fn hover_for_param_edge() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 foo: i32 ``` "#]], @@ -1169,7 +1261,7 @@ fn main() { let foo_$0test = Thing::new(); } *foo_test* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let foo_test: Thing ``` "#]], @@ -1374,7 +1466,7 @@ fn y() { *x* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let x: i32 ``` "#]], @@ -1505,7 +1597,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } *bar* ```rust - // size = 4, align = 4 + // size = 4, align = 4 bar: u32 ``` "#]], @@ -1524,7 +1616,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } *bar* ```rust - // size = 4, align = 4 + // size = 4, align = 4 bar: u32 ``` "#]], @@ -1760,7 +1852,7 @@ fn test_hover_function_pointer_show_identifiers() { ``` ```rust - // size = 8, align = 8, niches = 1 + // size = 8, align = 8, niches = 1 type foo = fn(a: i32, b: i32) -> i32 ``` "#]], @@ -1779,7 +1871,7 @@ fn test_hover_function_pointer_no_identifier() { ``` ```rust - // size = 8, align = 8, niches = 1 + // size = 8, align = 8, niches = 1 type foo = fn(i32, i32) -> i32 ``` "#]], @@ -1926,7 +2018,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 struct Bar ``` @@ -1963,7 +2055,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 struct Bar ``` @@ -1993,7 +2085,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 struct Bar ``` @@ -2022,7 +2114,7 @@ pub struct B$0ar ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 pub struct Bar ``` @@ -2050,7 +2142,7 @@ pub struct B$0ar ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 pub struct Bar ``` @@ -2140,7 +2232,7 @@ fn test_hover_layout_of_variant() { ``` ```rust - // size = 4, align = 2 + // size = 4, align = 2 Variant1(u8, u16) ``` "#]], @@ -2162,7 +2254,7 @@ fn test_hover_layout_of_enum() { ``` ```rust - // size = 16 (0x10), align = 8, niches = 254 + // size = 16 (0x10), align = 8, niches = 254 enum Foo { Variant1(u8, u16), Variant2(i32, u8, i64), @@ -2540,7 +2632,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 7..10, name: "Arg", kind: Struct, - description: "struct Arg(u32);", + description: "struct Arg(u32)", }, }, HoverGotoTypeData { @@ -2599,7 +2691,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 7..10, name: "Arg", kind: Struct, - description: "struct Arg(u32);", + description: "struct Arg(u32)", }, }, HoverGotoTypeData { @@ -2648,7 +2740,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } focus_range: 7..8, name: "A", kind: Struct, - description: "struct A(u32);", + description: "struct A(u32)", }, }, HoverGotoTypeData { @@ -2661,7 +2753,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } focus_range: 22..23, name: "B", kind: Struct, - description: "struct B(u32);", + description: "struct B(u32)", }, }, HoverGotoTypeData { @@ -2675,7 +2767,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } name: "C", kind: Struct, container_name: "M", - description: "pub struct C(u32);", + description: "pub struct C(u32)", }, }, ], @@ -3331,26 +3423,26 @@ struct Foo; impl Foo {} "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::Bar", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..11, - focus_range: 7..10, - name: "Bar", - kind: Struct, - description: "struct Bar", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::Bar", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..11, + focus_range: 7..10, + name: "Bar", + kind: Struct, + description: "struct Bar", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -3396,26 +3488,26 @@ impl Foo { } "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::Foo", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..11, - focus_range: 7..10, - name: "Foo", - kind: Struct, - description: "struct Foo", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::Foo", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..11, + focus_range: 7..10, + name: "Foo", + kind: Struct, + description: "struct Foo", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -3466,7 +3558,7 @@ fn main() { *f* ```rust - // size = 8, align = 8, niches = 1 + // size = 8, align = 8, niches = 1 let f: &i32 ``` --- @@ -3476,7 +3568,7 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 f: i32 ``` "#]], @@ -3498,7 +3590,7 @@ struct S$0T(T); ``` ```rust - struct ST(T); + struct ST(T) ``` "#]], ); @@ -3519,7 +3611,7 @@ struct S$0T(T); ``` ```rust - struct ST(T); + struct ST(T) ``` "#]], ); @@ -3541,7 +3633,7 @@ struct S$0T(T); ``` ```rust - struct ST(T); + struct ST(T) ``` "#]], ); @@ -3561,7 +3653,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const<1> ``` "#]], @@ -3582,7 +3674,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const<0> ``` "#]], @@ -3603,7 +3695,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const<-1> ``` "#]], @@ -3624,7 +3716,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const ``` "#]], @@ -3645,7 +3737,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const<'🦀'> ``` "#]], @@ -3665,7 +3757,7 @@ impl Foo { *self* ```rust - // size = 8, align = 8, niches = 1 + // size = 8, align = 8, niches = 1 self: &Foo ``` "#]], @@ -3686,7 +3778,7 @@ impl Foo { *self* ```rust - // size = 0, align = 1 + // size = 0, align = 1 self: Arc ``` "#]], @@ -4072,7 +4164,7 @@ type Fo$0o2 = Foo<2>; ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 type Foo2 = Foo<2> ``` "#]], @@ -4115,7 +4207,7 @@ enum E { ``` ```rust - // size = 1, align = 1 + // size = 1, align = 1 A = 8 ``` @@ -4141,7 +4233,7 @@ enum E { ``` ```rust - // size = 1, align = 1 + // size = 1, align = 1 A = 12 (0xC) ``` @@ -4168,7 +4260,7 @@ enum E { ``` ```rust - // size = 1, align = 1 + // size = 1, align = 1 B = 2 ``` @@ -4195,7 +4287,7 @@ enum E { ``` ```rust - // size = 1, align = 1 + // size = 1, align = 1 B = 5 ``` @@ -5002,7 +5094,7 @@ fn foo(e: E) { ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 A = 3 ``` @@ -5025,7 +5117,7 @@ fn main() { *tile4* ```rust - // size = 32 (0x20), align = 4 + // size = 32 (0x20), align = 4 let tile4: [u32; 8] ``` "#]], @@ -5262,7 +5354,7 @@ pub fn gimme() -> theitem::TheItem { ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 pub struct TheItem ``` @@ -5411,7 +5503,7 @@ mod string { ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 struct String ``` @@ -5931,26 +6023,26 @@ fn foo() { } "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::Foo", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..11, - focus_range: 7..10, - name: "Foo", - kind: Struct, - description: "struct Foo", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::Foo", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..11, + focus_range: 7..10, + name: "Foo", + kind: Struct, + description: "struct Foo", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -6139,7 +6231,7 @@ foo_macro!( ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 pub struct Foo ``` @@ -6165,8 +6257,8 @@ pub struct Foo(i32); ``` ```rust - // size = 4, align = 4 - pub struct Foo(i32); + // size = 4, align = 4 + pub struct Foo(i32) ``` --- @@ -6191,7 +6283,7 @@ pub struct Foo(T); ``` ```rust - pub struct Foo(T); + pub struct Foo(T) ``` --- @@ -6290,7 +6382,7 @@ enum Enum { ``` ```rust - // size = 4, align = 4 + // size = 4, align = 4 RecordV { field: u32 } ``` "#]], @@ -6313,7 +6405,7 @@ enum Enum { ``` ```rust - // size = 4, align = 4 + // size = 4, align = 4 field: u32 ``` "#]], @@ -6961,7 +7053,7 @@ fn test() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 f: u32 ``` "#]], @@ -6981,7 +7073,7 @@ fn test() { *s* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let s: S ``` "#]], @@ -7002,7 +7094,7 @@ fn test() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let foo: i32 ``` "#]], @@ -7023,7 +7115,7 @@ format_args!("{aaaaa$0}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 + // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7044,7 +7136,7 @@ format_args!("{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 + // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7065,7 +7157,7 @@ format_args!(r"{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 + // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7091,7 +7183,7 @@ foo!(r"{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 + // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7440,8 +7532,8 @@ fn main(notable$0: u32) {} *notable* ```rust - // Implements notable traits: Notable - // size = 4, align = 4 + // Implements notable traits: Notable + // size = 4, align = 4 notable: u32 ``` "#]], @@ -7472,8 +7564,8 @@ impl Iterator for S { ``` ```rust - // Implements notable traits: Notable, Future, Iterator - // size = 0, align = 1 + // Implements notable traits: Notable, Future, Iterator + // size = 0, align = 1 struct S ``` "#]], @@ -7532,7 +7624,7 @@ extern "C" { ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 type Ty ``` "#]], @@ -7560,7 +7652,7 @@ fn main() { "#, expect![[r#" ```rust - // Implements notable traits: Notable, Future, Iterator + // Implements notable traits: Notable, Future, Iterator S ```"#]], ); diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs index 8d9ad5bda143..5ba4e514e1f0 100644 --- a/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/crates/ide/src/inlay_hints/implicit_drop.rs @@ -74,6 +74,10 @@ pub(super) fn hints( Ok(s) => s.value.text_range(), Err(_) => continue, }, + MirSpan::SelfParam => match source_map.self_param_syntax() { + Some(s) => s.value.text_range(), + None => continue, + }, MirSpan::Unknown => continue, }; let binding = &hir.bindings[*binding]; diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 59a7df14fd53..6955e14a10a1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -259,7 +259,7 @@ impl Analysis { false, CrateOrigin::Local { repo: None, name: None }, ); - change.change_file(file_id, Some(Arc::from(text))); + change.change_file(file_id, Some(text)); change.set_crate_graph(crate_graph); change.set_target_data_layouts(vec![Err("fixture has no layout".into())]); change.set_toolchains(vec![None]); diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 674ce6d52bf7..2123c98605db 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -176,14 +176,12 @@ impl NavigationTarget { impl TryToNav for FileSymbol { fn try_to_nav(&self, db: &RootDatabase) -> Option> { - let root = db.parse_or_expand(self.loc.hir_file_id); - self.loc.ptr.to_node(&root); Some( - orig_range_with_focus( + orig_range_with_focus_r( db, self.loc.hir_file_id, - &self.loc.ptr.to_node(&root), - Some(self.loc.name_ptr.to_node(&root)), + self.loc.ptr.text_range(), + Some(self.loc.name_ptr.text_range()), ) .map(|(FileRange { file_id, range: full_range }, focus_range)| { NavigationTarget { @@ -722,7 +720,21 @@ fn orig_range_with_focus( value: &SyntaxNode, name: Option, ) -> UpmappingResult<(FileRange, Option)> { - let Some(name) = name else { return orig_range(db, hir_file, value) }; + orig_range_with_focus_r( + db, + hir_file, + value.text_range(), + name.map(|it| it.syntax().text_range()), + ) +} + +fn orig_range_with_focus_r( + db: &RootDatabase, + hir_file: HirFileId, + value: TextRange, + name: Option, +) -> UpmappingResult<(FileRange, Option)> { + let Some(name) = name else { return orig_range_r(db, hir_file, value) }; let call_kind = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id).kind; @@ -733,9 +745,9 @@ fn orig_range_with_focus( .definition_range(db) }; - let value_range = InFile::new(hir_file, value).original_file_range_opt(db); + let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db); let ((call_site_range, call_site_focus), def_site) = - match InFile::new(hir_file, name.syntax()).original_file_range_opt(db) { + match InFile::new(hir_file, name).original_node_file_range_opt(db) { // call site name Some((focus_range, ctxt)) if ctxt.is_root() => { // Try to upmap the node as well, if it ends up in the def site, go back to the call site @@ -802,7 +814,7 @@ fn orig_range_with_focus( } } // lost name? can't happen for single tokens - None => return orig_range(db, hir_file, value), + None => return orig_range_r(db, hir_file, value), }; UpmappingResult { @@ -840,7 +852,18 @@ fn orig_range( value: &SyntaxNode, ) -> UpmappingResult<(FileRange, Option)> { UpmappingResult { - call_site: (InFile::new(hir_file, value).original_file_range(db), None), + call_site: (InFile::new(hir_file, value).original_file_range_rooted(db), None), + def_site: None, + } +} + +fn orig_range_r( + db: &RootDatabase, + hir_file: HirFileId, + value: TextRange, +) -> UpmappingResult<(FileRange, Option)> { + UpmappingResult { + call_site: (InFile::new(hir_file, value).original_node_file_range(db).0, None), def_site: None, } } diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index dcdc6118a348..fef2aba3c616 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -1710,7 +1710,7 @@ use proc_macros::mirror; mirror$0! {} "#, expect![[r#" - mirror Macro FileId(1) 1..77 22..28 + mirror ProcMacro FileId(1) 1..77 22..28 FileId(0) 26..32 "#]], diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 5fe46444ff41..79324bf3877b 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -138,7 +138,9 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { }) { if let Some(def) = def { let file_id = match def { - Definition::Module(it) => it.declaration_source(db).map(|src| src.file_id), + Definition::Module(it) => { + it.declaration_source_range(db).map(|src| src.file_id) + } Definition::Function(it) => it.source(db).map(|src| src.file_id), _ => None, }; @@ -269,15 +271,10 @@ fn find_related_tests_in_module( Some(it) => it, _ => return, }; - let mod_source = parent_module.definition_source(sema.db); - let range = match &mod_source.value { - hir::ModuleSource::Module(m) => m.syntax().text_range(), - hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(), - hir::ModuleSource::SourceFile(f) => f.syntax().text_range(), - }; + let mod_source = parent_module.definition_source_range(sema.db); let file_id = mod_source.file_id.original_file(sema.db); - let mod_scope = SearchScope::file_range(FileRange { file_id, range }); + let mod_scope = SearchScope::file_range(FileRange { file_id, range: mod_source.value }); let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() }; find_related_tests(sema, syntax, fn_pos, Some(mod_scope), tests) } @@ -405,14 +402,15 @@ fn runnable_mod_outline_definition( let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); - match def.definition_source(sema.db).value { - hir::ModuleSource::SourceFile(_) => Some(Runnable { + if def.as_source_file_id(sema.db).is_some() { + Some(Runnable { use_name_in_title: false, nav: def.to_nav(sema.db).call_site(), kind: RunnableKind::TestMod { path }, cfg, - }), - _ => None, + }) + } else { + None } } diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index d2bd3bab14e4..6aaf8f8e7795 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -248,6 +248,7 @@ fn traverse( // an attribute nested in a macro call will not emit `inside_attribute` let mut inside_attribute = false; let mut inside_macro_call = false; + let mut inside_proc_macro_call = false; // Walk all nodes, keeping track of whether we are inside a macro or not. // If in macro, expand it first and highlight the expanded code. @@ -298,8 +299,9 @@ fn traverse( ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => { bindings_shadow_count.clear() } - ast::Item::MacroCall(_) => { + ast::Item::MacroCall(ref macro_call) => { inside_macro_call = true; + inside_proc_macro_call = sema.is_proc_macro_call(macro_call); } _ => (), } @@ -344,6 +346,7 @@ fn traverse( } Some(ast::Item::MacroCall(_)) => { inside_macro_call = false; + inside_proc_macro_call = false; } _ => (), } @@ -519,6 +522,9 @@ fn traverse( highlight |= HlMod::Attribute } if inside_macro_call && tt_level > 0 { + if inside_proc_macro_call { + highlight |= HlMod::ProcMacro + } highlight |= HlMod::Macro } diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index bbc6b55a6422..a6dca0541e5f 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs @@ -98,6 +98,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 6d4cdd0efe21..5163a0de4177 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -75,6 +75,7 @@ pub enum HlMod { Library, /// Used to differentiate individual elements within macro calls. Macro, + ProcMacro, /// Mutable binding. Mutable, /// Used for public items. @@ -146,6 +147,7 @@ impl HlTag { SymbolKind::LifetimeParam => "lifetime", SymbolKind::Local => "variable", SymbolKind::Macro => "macro", + SymbolKind::ProcMacro => "proc_macro", SymbolKind::Module => "module", SymbolKind::SelfParam => "self_keyword", SymbolKind::SelfType => "self_type_keyword", @@ -219,6 +221,7 @@ impl HlMod { HlMod::IntraDocLink, HlMod::Library, HlMod::Macro, + HlMod::ProcMacro, HlMod::Mutable, HlMod::Public, HlMod::Reference, @@ -243,6 +246,7 @@ impl HlMod { HlMod::IntraDocLink => "intra_doc_link", HlMod::Library => "library", HlMod::Macro => "macro", + HlMod::ProcMacro => "proc_macro", HlMod::Mutable => "mutable", HlMod::Public => "public", HlMod::Reference => "reference", diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index 4dcbfe4eb62c..6994cb3d5c54 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index bf5505caf376..dc2d103b5815 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html index 977d18c6b734..093cc2358a67 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html index 0d1b3c1f1831..154b823fffb1 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index dd1528ed03f0..58613bf15105 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index d5f92aa5d478..34274932afc1 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 88a008796b37..729e4791f557 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index bdeb09d2f83c..066fcfb1dfe6 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index f9c33b8a6017..58a147dd80a4 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 2043752bc746..22ae5c82a4b5 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html index ec39998de268..af7799965937 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 4063cf9f7570..32ac6a94d868 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } @@ -45,12 +46,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
use proc_macros::{mirror, identity, DeriveIdentity};
 
-mirror! {
-    {
-        ,i32 :x pub
-        ,i32 :y pub
-    } Foo struct
-}
+mirror! {
+    {
+        ,i32 :x pub
+        ,i32 :y pub
+    } Foo struct
+}
 macro_rules! def_fn {
     ($($tt:tt)*) => {$($tt)*}
 }
@@ -93,7 +94,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 
-include!(concat!("foo/", "foo.rs"));
+include!(concat!("foo/", "foo.rs"));
 
 fn main() {
     format_args!("Hello, {}!", (92,).0);
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
index 4dcf8e5f01f9..ef8a48ca1c1d 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
index 084bbf2f7420..a2ded15fd1ba 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
index 1af4bcfbd9dd..d123ee049765 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
index 7ee7b338c19a..4429e5d933ac 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 84a823363f68..b6458fa7ca09 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index c72ea54e948e..49b588baa58e 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/test_explorer.rs b/crates/ide/src/test_explorer.rs
index 2e741021ea8b..ca4713997030 100644
--- a/crates/ide/src/test_explorer.rs
+++ b/crates/ide/src/test_explorer.rs
@@ -11,7 +11,7 @@ use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav
 
 #[derive(Debug)]
 pub enum TestItemKind {
-    Crate,
+    Crate(CrateId),
     Module,
     Function,
 }
@@ -32,15 +32,17 @@ pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec {
     crate_graph
         .iter()
         .filter(|&id| crate_graph[id].origin.is_local())
-        .filter_map(|id| Some(crate_graph[id].display_name.as_ref()?.to_string()))
-        .map(|id| TestItem {
-            kind: TestItemKind::Crate,
-            label: id.clone(),
-            id,
-            parent: None,
-            file: None,
-            text_range: None,
-            runnable: None,
+        .filter_map(|id| {
+            let test_id = crate_graph[id].display_name.as_ref()?.to_string();
+            Some(TestItem {
+                kind: TestItemKind::Crate(id),
+                label: test_id.clone(),
+                id: test_id,
+                parent: None,
+                file: None,
+                text_range: None,
+                runnable: None,
+            })
         })
         .collect()
 }
@@ -118,12 +120,13 @@ pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> V
     let Some(crate_test_id) = &crate_graph[crate_id].display_name else {
         return vec![];
     };
+    let kind = TestItemKind::Crate(crate_id);
     let crate_test_id = crate_test_id.to_string();
     let crate_id: Crate = crate_id.into();
     let module = crate_id.root_module();
     let mut r = vec![TestItem {
         id: crate_test_id.clone(),
-        kind: TestItemKind::Crate,
+        kind,
         label: crate_test_id.clone(),
         parent: None,
         file: None,
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index a1c089520da5..fe680e47fef8 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -361,8 +361,8 @@ fn load_crate_graph(
     let changes = vfs.take_changes();
     for file in changes {
         if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change {
-            if let Ok(text) = std::str::from_utf8(&v) {
-                analysis_change.change_file(file.file_id, Some(text.into()))
+            if let Ok(text) = String::from_utf8(v) {
+                analysis_change.change_file(file.file_id, Some(text))
             }
         }
     }
@@ -419,14 +419,10 @@ impl ProcMacroExpander for Expander {
 #[cfg(test)]
 mod tests {
     use ide_db::base_db::SourceDatabase;
+    use vfs::file_set::FileSetConfigBuilder;
 
     use super::*;
 
-    use ide_db::base_db::SourceRootId;
-    use vfs::{file_set::FileSetConfigBuilder, VfsPath};
-
-    use crate::SourceRootConfig;
-
     #[test]
     fn test_loading_rust_analyzer() {
         let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index d946ecc1caf5..ac7f07117845 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -1,6 +1,7 @@
 //! This module add real world mbe example for benchmark tests
 
 use rustc_hash::FxHashMap;
+use span::Span;
 use syntax::{
     ast::{self, HasName},
     AstNode, SmolStr,
@@ -9,7 +10,7 @@ use test_utils::{bench, bench_fixture, skip_slow_tests};
 
 use crate::{
     parser::{MetaVarKind, Op, RepeatKind, Separator},
-    syntax_node_to_token_tree, DeclarativeMacro, DummyTestSpanData, DummyTestSpanMap, DUMMY,
+    syntax_node_to_token_tree, DeclarativeMacro, DummyTestSpanMap, DUMMY,
 };
 
 #[test]
@@ -50,14 +51,14 @@ fn benchmark_expand_macro_rules() {
     assert_eq!(hash, 69413);
 }
 
-fn macro_rules_fixtures() -> FxHashMap> {
+fn macro_rules_fixtures() -> FxHashMap> {
     macro_rules_fixtures_tt()
         .into_iter()
         .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true)))
         .collect()
 }
 
-fn macro_rules_fixtures_tt() -> FxHashMap> {
+fn macro_rules_fixtures_tt() -> FxHashMap> {
     let fixture = bench_fixture::numerous_macro_rules();
     let source_file = ast::SourceFile::parse(&fixture).ok().unwrap();
 
@@ -79,8 +80,8 @@ fn macro_rules_fixtures_tt() -> FxHashMap
 
 /// Generate random invocation fixtures from rules
 fn invocation_fixtures(
-    rules: &FxHashMap>,
-) -> Vec<(String, tt::Subtree)> {
+    rules: &FxHashMap>,
+) -> Vec<(String, tt::Subtree)> {
     let mut seed = 123456789;
     let mut res = Vec::new();
 
@@ -128,8 +129,8 @@ fn invocation_fixtures(
     return res;
 
     fn collect_from_op(
-        op: &Op,
-        token_trees: &mut Vec>,
+        op: &Op,
+        token_trees: &mut Vec>,
         seed: &mut usize,
     ) {
         return match op {
@@ -221,19 +222,19 @@ fn invocation_fixtures(
             *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
             *seed
         }
-        fn make_ident(ident: &str) -> tt::TokenTree {
+        fn make_ident(ident: &str) -> tt::TokenTree {
             tt::Leaf::Ident(tt::Ident { span: DUMMY, text: SmolStr::new(ident) }).into()
         }
-        fn make_punct(char: char) -> tt::TokenTree {
+        fn make_punct(char: char) -> tt::TokenTree {
             tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }).into()
         }
-        fn make_literal(lit: &str) -> tt::TokenTree {
+        fn make_literal(lit: &str) -> tt::TokenTree {
             tt::Leaf::Literal(tt::Literal { span: DUMMY, text: SmolStr::new(lit) }).into()
         }
         fn make_subtree(
             kind: tt::DelimiterKind,
-            token_trees: Option>>,
-        ) -> tt::TokenTree {
+            token_trees: Option>>,
+        ) -> tt::TokenTree {
             tt::Subtree {
                 delimiter: tt::Delimiter { open: DUMMY, close: DUMMY, kind },
                 token_trees: token_trees.map(Vec::into_boxed_slice).unwrap_or_default(),
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 3c270e30a9ba..57d6082dd705 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -23,8 +23,11 @@ pub trait SpanMapper {
     fn span_for(&self, range: TextRange) -> S;
 }
 
-impl SpanMapper for SpanMap {
-    fn span_for(&self, range: TextRange) -> S {
+impl SpanMapper> for SpanMap
+where
+    SpanData: Span,
+{
+    fn span_for(&self, range: TextRange) -> SpanData {
         self.span_at(range.start())
     }
 }
@@ -38,32 +41,30 @@ impl> SpanMapper for &SM {
 /// Dummy things for testing where spans don't matter.
 pub(crate) mod dummy_test_span_utils {
 
+    use span::{Span, SyntaxContextId};
+
     use super::*;
 
-    pub type DummyTestSpanData = span::SpanData;
-    pub const DUMMY: DummyTestSpanData = span::SpanData {
+    pub const DUMMY: Span = Span {
         range: TextRange::empty(TextSize::new(0)),
         anchor: span::SpanAnchor {
             file_id: span::FileId::BOGUS,
             ast_id: span::ROOT_ERASED_FILE_AST_ID,
         },
-        ctx: DummyTestSyntaxContext,
+        ctx: SyntaxContextId::ROOT,
     };
 
-    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
-    pub struct DummyTestSyntaxContext;
-
     pub struct DummyTestSpanMap;
 
-    impl SpanMapper> for DummyTestSpanMap {
-        fn span_for(&self, range: syntax::TextRange) -> span::SpanData {
-            span::SpanData {
+    impl SpanMapper for DummyTestSpanMap {
+        fn span_for(&self, range: syntax::TextRange) -> Span {
+            Span {
                 range,
                 anchor: span::SpanAnchor {
                     file_id: span::FileId::BOGUS,
                     ast_id: span::ROOT_ERASED_FILE_AST_ID,
                 },
-                ctx: DummyTestSyntaxContext,
+                ctx: SyntaxContextId::ROOT,
             }
         }
     }
@@ -92,7 +93,7 @@ pub fn syntax_node_to_token_tree_modified(
     node: &SyntaxNode,
     map: SpanMap,
     append: FxHashMap>>>,
-    remove: FxHashSet,
+    remove: FxHashSet,
     call_site: SpanData,
 ) -> tt::Subtree>
 where
@@ -121,7 +122,7 @@ where
 pub fn token_tree_to_syntax_node(
     tt: &tt::Subtree>,
     entry_point: parser::TopEntryPoint,
-) -> (Parse, SpanMap>)
+) -> (Parse, SpanMap)
 where
     SpanData: Span,
     Ctx: Copy,
@@ -629,7 +630,7 @@ struct Converter {
     /// Used to make the emitted text ranges in the spans relative to the span anchor.
     map: SpanMap,
     append: FxHashMap>>,
-    remove: FxHashSet,
+    remove: FxHashSet,
     call_site: S,
 }
 
@@ -638,7 +639,7 @@ impl Converter {
         node: &SyntaxNode,
         map: SpanMap,
         append: FxHashMap>>,
-        remove: FxHashSet,
+        remove: FxHashSet,
         call_site: S,
     ) -> Self {
         let mut this = Converter {
@@ -660,16 +661,25 @@ impl Converter {
     fn next_token(&mut self) -> Option {
         while let Some(ev) = self.preorder.next() {
             match ev {
-                WalkEvent::Enter(SyntaxElement::Token(t)) => return Some(t),
-                WalkEvent::Enter(SyntaxElement::Node(n)) if self.remove.contains(&n) => {
-                    self.preorder.skip_subtree();
-                    if let Some(mut v) = self.append.remove(&n.into()) {
-                        v.reverse();
-                        self.current_leaves.extend(v);
-                        return None;
+                WalkEvent::Enter(token) => {
+                    if self.remove.contains(&token) {
+                        match token {
+                            syntax::NodeOrToken::Token(_) => {
+                                continue;
+                            }
+                            node => {
+                                self.preorder.skip_subtree();
+                                if let Some(mut v) = self.append.remove(&node) {
+                                    v.reverse();
+                                    self.current_leaves.extend(v);
+                                    return None;
+                                }
+                            }
+                        }
+                    } else if let syntax::NodeOrToken::Token(token) = token {
+                        return Some(token);
                     }
                 }
-                WalkEvent::Enter(SyntaxElement::Node(_)) => (),
                 WalkEvent::Leave(ele) => {
                     if let Some(mut v) = self.append.remove(&ele) {
                         v.reverse();
@@ -824,7 +834,7 @@ where
     cursor: Cursor<'a, SpanData>,
     text_pos: TextSize,
     inner: SyntaxTreeBuilder,
-    token_map: SpanMap>,
+    token_map: SpanMap,
 }
 
 impl<'a, Ctx> TtTreeSink<'a, Ctx>
@@ -841,7 +851,7 @@ where
         }
     }
 
-    fn finish(mut self) -> (Parse, SpanMap>) {
+    fn finish(mut self) -> (Parse, SpanMap) {
         self.token_map.finish();
         (self.inner.finish(), self.token_map)
     }
diff --git a/crates/mbe/src/syntax_bridge/tests.rs b/crates/mbe/src/syntax_bridge/tests.rs
index 11d1a7287996..a261b1d43193 100644
--- a/crates/mbe/src/syntax_bridge/tests.rs
+++ b/crates/mbe/src/syntax_bridge/tests.rs
@@ -1,4 +1,5 @@
 use rustc_hash::FxHashMap;
+use span::Span;
 use syntax::{ast, AstNode};
 use test_utils::extract_annotations;
 use tt::{
@@ -6,7 +7,7 @@ use tt::{
     Leaf, Punct, Spacing,
 };
 
-use crate::{syntax_node_to_token_tree, DummyTestSpanData, DummyTestSpanMap, DUMMY};
+use crate::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY};
 
 fn check_punct_spacing(fixture: &str) {
     let source_file = ast::SourceFile::parse(fixture).ok().unwrap();
@@ -28,7 +29,7 @@ fn check_punct_spacing(fixture: &str) {
     while !cursor.eof() {
         while let Some(token_tree) = cursor.token_tree() {
             if let TokenTreeRef::Leaf(
-                Leaf::Punct(Punct { spacing, span: DummyTestSpanData { range, .. }, .. }),
+                Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }),
                 _,
             ) = token_tree
             {
diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs
index 54a20357d262..11b008fc0b4b 100644
--- a/crates/proc-macro-srv/src/tests/mod.rs
+++ b/crates/proc-macro-srv/src/tests/mod.rs
@@ -8,7 +8,12 @@ use expect_test::expect;
 
 #[test]
 fn test_derive_empty() {
-    assert_expand("DeriveEmpty", r#"struct S;"#, expect!["SUBTREE $$ 1 1"], expect!["SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"]);
+    assert_expand(
+        "DeriveEmpty",
+        r#"struct S;"#,
+        expect!["SUBTREE $$ 1 1"],
+        expect!["SUBTREE $$ 42:2@0..100#0 42:2@0..100#0"],
+    );
 }
 
 #[test]
@@ -21,15 +26,15 @@ fn test_derive_error() {
               IDENT   compile_error 1
               PUNCH   ! [alone] 1
               SUBTREE () 1 1
-                LITERAL "#[derive(DeriveError)] struct S ;" 1
+                LITERAL "#[derive(DeriveError)] struct S ;"1
               PUNCH   ; [alone] 1"##]],
         expect![[r##"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   compile_error SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   ! [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              SUBTREE () SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-                LITERAL "#[derive(DeriveError)] struct S ;" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   ; [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"##]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   compile_error 42:2@0..100#0
+              PUNCH   ! [alone] 42:2@0..100#0
+              SUBTREE () 42:2@0..100#0 42:2@0..100#0
+                LITERAL "#[derive(DeriveError)] struct S ;"42:2@0..100#0
+              PUNCH   ; [alone] 42:2@0..100#0"##]],
     );
 }
 
@@ -42,20 +47,20 @@ fn test_fn_like_macro_noop() {
             SUBTREE $$ 1 1
               IDENT   ident 1
               PUNCH   , [alone] 1
-              LITERAL 0 1
+              LITERAL 01
               PUNCH   , [alone] 1
-              LITERAL 1 1
+              LITERAL 11
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   ident SpanData { range: 0..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 5..6, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 0 SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 8..9, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 1 SpanData { range: 10..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 11..12, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              SUBTREE [] SpanData { range: 13..14, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 14..15, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   ident 42:2@0..5#0
+              PUNCH   , [alone] 42:2@5..6#0
+              LITERAL 042:2@7..8#0
+              PUNCH   , [alone] 42:2@8..9#0
+              LITERAL 142:2@10..11#0
+              PUNCH   , [alone] 42:2@11..12#0
+              SUBTREE [] 42:2@13..14#0 42:2@14..15#0"#]],
     );
 }
 
@@ -70,10 +75,10 @@ fn test_fn_like_macro_clone_ident_subtree() {
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   ident SpanData { range: 0..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 5..6, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              SUBTREE [] SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   ident 42:2@0..5#0
+              PUNCH   , [alone] 42:2@5..6#0
+              SUBTREE [] 42:2@7..8#0 42:2@7..8#0"#]],
     );
 }
 
@@ -86,8 +91,8 @@ fn test_fn_like_macro_clone_raw_ident() {
             SUBTREE $$ 1 1
               IDENT   r#async 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   r#async SpanData { range: 0..7, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   r#async 42:2@0..7#0"#]],
     );
 }
 
@@ -100,8 +105,8 @@ fn test_fn_like_fn_like_span_join() {
             SUBTREE $$ 1 1
               IDENT   r#joined 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   r#joined SpanData { range: 0..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   r#joined 42:2@0..11#0"#]],
     );
 }
 
@@ -116,10 +121,10 @@ fn test_fn_like_fn_like_span_ops() {
               IDENT   resolved_at_def_site 1
               IDENT   start_span 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   set_def_site SpanData { range: 0..150, anchor: SpanAnchor(FileId(41), 1), ctx: SyntaxContextId(0) }
-              IDENT   resolved_at_def_site SpanData { range: 13..33, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   start_span SpanData { range: 34..34, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   set_def_site 41:1@0..150#0
+              IDENT   resolved_at_def_site 42:2@13..33#0
+              IDENT   start_span 42:2@34..34#0"#]],
     );
 }
 
@@ -130,22 +135,22 @@ fn test_fn_like_mk_literals() {
         r#""#,
         expect![[r#"
             SUBTREE $$ 1 1
-              LITERAL b"byte_string" 1
-              LITERAL 'c' 1
-              LITERAL "string" 1
-              LITERAL 3.14f64 1
-              LITERAL 3.14 1
-              LITERAL 123i64 1
-              LITERAL 123 1"#]],
+              LITERAL b"byte_string"1
+              LITERAL 'c'1
+              LITERAL "string"1
+              LITERAL 3.14f641
+              LITERAL 3.141
+              LITERAL 123i641
+              LITERAL 1231"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL b"byte_string" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 'c' SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL "string" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 3.14f64 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 3.14 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 123i64 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 123 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              LITERAL b"byte_string"42:2@0..100#0
+              LITERAL 'c'42:2@0..100#0
+              LITERAL "string"42:2@0..100#0
+              LITERAL 3.14f6442:2@0..100#0
+              LITERAL 3.1442:2@0..100#0
+              LITERAL 123i6442:2@0..100#0
+              LITERAL 12342:2@0..100#0"#]],
     );
 }
 
@@ -159,9 +164,9 @@ fn test_fn_like_mk_idents() {
               IDENT   standard 1
               IDENT   r#raw 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   standard SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   r#raw SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   standard 42:2@0..100#0
+              IDENT   r#raw 42:2@0..100#0"#]],
     );
 }
 
@@ -172,48 +177,48 @@ fn test_fn_like_macro_clone_literals() {
         r###"1u16, 2_u32, -4i64, 3.14f32, "hello bridge", "suffixed"suffix, r##"raw"##, 'a', b'b', c"null""###,
         expect![[r###"
             SUBTREE $$ 1 1
-              LITERAL 1u16 1
+              LITERAL 1u161
               PUNCH   , [alone] 1
-              LITERAL 2_u32 1
+              LITERAL 2_u321
               PUNCH   , [alone] 1
               PUNCH   - [alone] 1
-              LITERAL 4i64 1
+              LITERAL 4i641
               PUNCH   , [alone] 1
-              LITERAL 3.14f32 1
+              LITERAL 3.14f321
               PUNCH   , [alone] 1
-              LITERAL "hello bridge" 1
+              LITERAL "hello bridge"1
               PUNCH   , [alone] 1
-              LITERAL "suffixed"suffix 1
+              LITERAL "suffixed"suffix1
               PUNCH   , [alone] 1
-              LITERAL r##"raw"## 1
+              LITERAL r##"raw"##1
               PUNCH   , [alone] 1
-              LITERAL 'a' 1
+              LITERAL 'a'1
               PUNCH   , [alone] 1
-              LITERAL b'b' 1
+              LITERAL b'b'1
               PUNCH   , [alone] 1
-              LITERAL c"null" 1"###]],
+              LITERAL c"null"1"###]],
         expect![[r###"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 1u16 SpanData { range: 0..4, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 4..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 2_u32 SpanData { range: 6..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 11..12, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   - [alone] SpanData { range: 13..14, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 4i64 SpanData { range: 14..18, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 18..19, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 3.14f32 SpanData { range: 20..27, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 27..28, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL "hello bridge" SpanData { range: 29..43, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 43..44, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL "suffixed"suffix SpanData { range: 45..61, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 61..62, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL r##"raw"## SpanData { range: 63..73, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 73..74, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 'a' SpanData { range: 75..78, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 78..79, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL b'b' SpanData { range: 80..84, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 84..85, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL c"null" SpanData { range: 86..93, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"###]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              LITERAL 1u1642:2@0..4#0
+              PUNCH   , [alone] 42:2@4..5#0
+              LITERAL 2_u3242:2@6..11#0
+              PUNCH   , [alone] 42:2@11..12#0
+              PUNCH   - [alone] 42:2@13..14#0
+              LITERAL 4i6442:2@14..18#0
+              PUNCH   , [alone] 42:2@18..19#0
+              LITERAL 3.14f3242:2@20..27#0
+              PUNCH   , [alone] 42:2@27..28#0
+              LITERAL "hello bridge"42:2@29..43#0
+              PUNCH   , [alone] 42:2@43..44#0
+              LITERAL "suffixed"suffix42:2@45..61#0
+              PUNCH   , [alone] 42:2@61..62#0
+              LITERAL r##"raw"##42:2@63..73#0
+              PUNCH   , [alone] 42:2@73..74#0
+              LITERAL 'a'42:2@75..78#0
+              PUNCH   , [alone] 42:2@78..79#0
+              LITERAL b'b'42:2@80..84#0
+              PUNCH   , [alone] 42:2@84..85#0
+              LITERAL c"null"42:2@86..93#0"###]],
     );
 }
 
@@ -231,15 +236,15 @@ fn test_attr_macro() {
               IDENT   compile_error 1
               PUNCH   ! [alone] 1
               SUBTREE () 1 1
-                LITERAL "#[attr_error(some arguments)] mod m {}" 1
+                LITERAL "#[attr_error(some arguments)] mod m {}"1
               PUNCH   ; [alone] 1"##]],
         expect![[r##"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   compile_error SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   ! [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              SUBTREE () SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-                LITERAL "#[attr_error(some arguments)] mod m {}" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   ; [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"##]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   compile_error 42:2@0..100#0
+              PUNCH   ! [alone] 42:2@0..100#0
+              SUBTREE () 42:2@0..100#0 42:2@0..100#0
+                LITERAL "#[attr_error(some arguments)] mod m {}"42:2@0..100#0
+              PUNCH   ; [alone] 42:2@0..100#0"##]],
     );
 }
 
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs
index 9a1311d9550a..6050bc9e36ec 100644
--- a/crates/proc-macro-srv/src/tests/utils.rs
+++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -91,7 +91,7 @@ fn assert_expand_impl(
     let res = expander
         .expand(macro_name, fixture.into_subtree(call_site), attr, def_site, call_site, mixed_site)
         .unwrap();
-    expect_s.assert_eq(&format!("{res:?}"));
+    expect_s.assert_eq(&format!("{res:#?}"));
 }
 
 pub(crate) fn list() -> Vec {
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 766606be7bea..e6984d6f41b7 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -42,6 +42,7 @@ triomphe.workspace = true
 nohash-hasher.workspace = true
 always-assert = "0.2.0"
 walkdir = "2.3.2"
+semver.workspace = true
 
 cfg.workspace = true
 flycheck.workspace = true
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 07e04a836617..e747ec87b1c1 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -16,6 +16,7 @@ use std::{env, fs, path::PathBuf, process::ExitCode, sync::Arc};
 use anyhow::Context;
 use lsp_server::Connection;
 use rust_analyzer::{cli::flags, config::Config, from_json};
+use semver::Version;
 use tracing_subscriber::fmt::writer::BoxMakeWriter;
 use vfs::AbsPathBuf;
 
@@ -193,10 +194,18 @@ fn run_server() -> anyhow::Result<()> {
         }
     };
 
-    let mut is_visual_studio_code = false;
+    let mut visual_studio_code_version = None;
     if let Some(client_info) = client_info {
-        tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
-        is_visual_studio_code = client_info.name.starts_with("Visual Studio Code");
+        tracing::info!(
+            "Client '{}' {}",
+            client_info.name,
+            client_info.version.as_deref().unwrap_or_default()
+        );
+        visual_studio_code_version = client_info
+            .name
+            .starts_with("Visual Studio Code")
+            .then(|| client_info.version.as_deref().map(Version::parse).and_then(Result::ok))
+            .flatten();
     }
 
     let workspace_roots = workspace_folders
@@ -210,7 +219,8 @@ fn run_server() -> anyhow::Result<()> {
         })
         .filter(|workspaces| !workspaces.is_empty())
         .unwrap_or_else(|| vec![root_path.clone()]);
-    let mut config = Config::new(root_path, capabilities, workspace_roots, is_visual_studio_code);
+    let mut config =
+        Config::new(root_path, capabilities, workspace_roots, visual_studio_code_version);
     if let Some(json) = initialization_options {
         if let Err(e) = config.update(json) {
             use lsp_types::{
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index ef184032bfb0..5c474908e7ae 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1053,7 +1053,7 @@ fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id:
     };
     let root = db.parse_or_expand(src.file_id);
     let node = src.map(|e| e.to_node(&root).syntax().clone());
-    let original_range = node.as_ref().original_file_range(db);
+    let original_range = node.as_ref().original_file_range_rooted(db);
     let path = vfs.file_path(original_range.file_id);
     let line_index = db.line_index(original_range.file_id);
     let text_range = original_range.range;
@@ -1069,7 +1069,7 @@ fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: Pa
     };
     let root = db.parse_or_expand(src.file_id);
     let node = src.map(|e| e.to_node(&root).syntax().clone());
-    let original_range = node.as_ref().original_file_range(db);
+    let original_range = node.as_ref().original_file_range_rooted(db);
     let path = vfs.file_path(original_range.file_id);
     let line_index = db.line_index(original_range.file_id);
     let text_range = original_range.range;
@@ -1088,7 +1088,7 @@ fn expr_syntax_range<'a>(
     if let Ok(src) = src {
         let root = db.parse_or_expand(src.file_id);
         let node = src.map(|e| e.to_node(&root).syntax().clone());
-        let original_range = node.as_ref().original_file_range(db);
+        let original_range = node.as_ref().original_file_range_rooted(db);
         let path = vfs.file_path(original_range.file_id);
         let line_index = db.line_index(original_range.file_id);
         let text_range = original_range.range;
@@ -1109,7 +1109,7 @@ fn pat_syntax_range<'a>(
     if let Ok(src) = src {
         let root = db.parse_or_expand(src.file_id);
         let node = src.map(|e| e.to_node(&root).syntax().clone());
-        let original_range = node.as_ref().original_file_range(db);
+        let original_range = node.as_ref().original_file_range_rooted(db);
         let path = vfs.file_path(original_range.file_id);
         let line_index = db.line_index(original_range.file_id);
         let text_range = original_range.range;
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index 7ad87ab97fc6..84f2e6008746 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -134,7 +134,7 @@ impl Tester {
         let should_have_no_error = text.contains("// check-pass")
             || text.contains("// build-pass")
             || text.contains("// run-pass");
-        change.change_file(self.root_file, Some(Arc::from(text)));
+        change.change_file(self.root_file, Some(text));
         self.host.apply_change(change);
         let diagnostic_config = DiagnosticsConfig::test_sample();
 
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index 8fd59d159c9e..1061a433a58f 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -32,8 +32,8 @@ impl flags::Scip {
         let mut config = crate::config::Config::new(
             root.clone(),
             lsp_types::ClientCapabilities::default(),
-            /* workspace_roots = */ vec![],
-            /* is_visual_studio_code = */ false,
+            vec![],
+            None,
         );
 
         if let Some(p) = self.config_path {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 9e81c8dd665f..cbf152465909 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -31,6 +31,7 @@ use project_model::{
     CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
 };
 use rustc_hash::{FxHashMap, FxHashSet};
+use semver::Version;
 use serde::{de::DeserializeOwned, Deserialize};
 use stdx::format_to_acc;
 use vfs::{AbsPath, AbsPathBuf};
@@ -624,7 +625,7 @@ pub struct Config {
     data: ConfigData,
     detached_files: Vec,
     snippets: Vec,
-    is_visual_studio_code: bool,
+    visual_studio_code_version: Option,
 }
 
 type ParallelCachePrimingNumThreads = u8;
@@ -823,7 +824,7 @@ impl Config {
         root_path: AbsPathBuf,
         caps: ClientCapabilities,
         workspace_roots: Vec,
-        is_visual_studio_code: bool,
+        visual_studio_code_version: Option,
     ) -> Self {
         Config {
             caps,
@@ -833,7 +834,7 @@ impl Config {
             root_path,
             snippets: Default::default(),
             workspace_roots,
-            is_visual_studio_code,
+            visual_studio_code_version,
         }
     }
 
@@ -1778,10 +1779,10 @@ impl Config {
         self.data.typing_autoClosingAngleBrackets_enable
     }
 
-    // FIXME: VSCode seems to work wrong sometimes, see https://github.com/microsoft/vscode/issues/193124
-    // hence, distinguish it for now.
-    pub fn is_visual_studio_code(&self) -> bool {
-        self.is_visual_studio_code
+    // VSCode is our reference implementation, so we allow ourselves to work around issues by
+    // special casing certain versions
+    pub fn visual_studio_code_version(&self) -> Option<&Version> {
+        self.visual_studio_code_version.as_ref()
     }
 }
 // Deserialization definitions
@@ -2694,7 +2695,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2710,7 +2711,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2726,7 +2727,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2745,7 +2746,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2764,7 +2765,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2783,7 +2784,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index e900f2601d83..6e6cc53c251c 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -542,7 +542,7 @@ mod tests {
                 workspace_root.to_path_buf(),
                 ClientCapabilities::default(),
                 Vec::new(),
-                false,
+                None,
             ),
         );
         let snap = state.snapshot();
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 0e560e54eda3..1b4c33d85868 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -330,7 +330,7 @@ impl GlobalState {
                         // FIXME: Consider doing normalization in the `vfs` instead? That allows
                         // getting rid of some locking
                         let (text, line_endings) = LineEndings::normalize(text);
-                        (Arc::from(text), line_endings)
+                        (text, line_endings)
                     })
                 } else {
                     None
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 3bba4847f928..1c5a862c7035 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -20,7 +20,6 @@ use ide_db::{
 };
 use project_model::CargoConfig;
 use test_utils::project_root;
-use triomphe::Arc;
 use vfs::{AbsPathBuf, VfsPath};
 
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@@ -57,8 +56,6 @@ fn integrated_highlighting_benchmark() {
         vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
     };
 
-    let _g = crate::tracing::hprof::init("*>150");
-
     {
         let _it = stdx::timeit("initial");
         let analysis = host.analysis();
@@ -68,13 +65,16 @@ fn integrated_highlighting_benchmark() {
     {
         let _it = stdx::timeit("change");
         let mut text = host.analysis().file_text(file_id).unwrap().to_string();
-        text.push_str("\npub fn _dummy() {}\n");
+        text = text.replace(
+            "self.data.cargo_buildScripts_rebuildOnSave",
+            "self. data. cargo_buildScripts_rebuildOnSave",
+        );
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
     }
 
-    let _g = crate::tracing::hprof::init("*>50");
+    let _g = crate::tracing::hprof::init("*>20");
 
     {
         let _it = stdx::timeit("after change");
@@ -125,7 +125,7 @@ fn integrated_completion_benchmark() {
             patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
                 + "sel".len();
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
         completion_offset
     };
@@ -168,7 +168,7 @@ fn integrated_completion_benchmark() {
             patch(&mut text, "sel;\ndb.struct_data(self.id)", ";sel;\ndb.struct_data(self.id)")
                 + ";sel".len();
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
         completion_offset
     };
@@ -210,7 +210,7 @@ fn integrated_completion_benchmark() {
             patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)")
                 + "self.".len();
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
         completion_offset
     };
@@ -307,7 +307,7 @@ fn integrated_diagnostics_benchmark() {
         let mut text = host.analysis().file_text(file_id).unwrap().to_string();
         patch(&mut text, "db.struct_data(self.id)", "();\ndb.struct_data(self.id)");
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
     };
 
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index 86ab652f8ef5..710ce7f8acbb 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -234,6 +234,13 @@ impl Notification for EndRunTest {
     const METHOD: &'static str = "experimental/endRunTest";
 }
 
+pub enum AppendOutputToRunTest {}
+
+impl Notification for AppendOutputToRunTest {
+    type Params = String;
+    const METHOD: &'static str = "experimental/appendOutputToRunTest";
+}
+
 pub enum AbortRunTest {}
 
 impl Notification for AbortRunTest {
diff --git a/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/crates/rust-analyzer/src/lsp/semantic_tokens.rs
index dd7dcf527783..c5081c4bea02 100644
--- a/crates/rust-analyzer/src/lsp/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/lsp/semantic_tokens.rs
@@ -85,6 +85,7 @@ define_semantic_token_types![
         (LIFETIME, "lifetime"),
         (LOGICAL, "logical") => OPERATOR,
         (MACRO_BANG, "macroBang") => MACRO,
+        (PROC_MACRO, "procMacro") => MACRO,
         (PARENTHESIS, "parenthesis"),
         (PUNCTUATION, "punctuation"),
         (SELF_KEYWORD, "selfKeyword") => KEYWORD,
@@ -143,6 +144,7 @@ define_semantic_token_modifiers![
         (INTRA_DOC_LINK, "intraDocLink"),
         (LIBRARY, "library"),
         (MACRO_MODIFIER, "macro"),
+        (PROC_MACRO_MODIFIER, "proc_macro"),
         (MUTABLE, "mutable"),
         (PUBLIC, "public"),
         (REFERENCE, "reference"),
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index e2b55f4a5c5b..e77d0c13bf2d 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -15,6 +15,7 @@ use ide::{
 };
 use ide_db::rust_doc::format_docs;
 use itertools::Itertools;
+use semver::VersionReq;
 use serde_json::to_value;
 use vfs::AbsPath;
 
@@ -56,6 +57,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
         SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE,
         SymbolKind::Macro
+        | SymbolKind::ProcMacro
         | SymbolKind::BuiltinAttr
         | SymbolKind::Attribute
         | SymbolKind::Derive
@@ -138,6 +140,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::LifetimeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
             SymbolKind::Local => lsp_types::CompletionItemKind::VARIABLE,
             SymbolKind::Macro => lsp_types::CompletionItemKind::FUNCTION,
+            SymbolKind::ProcMacro => lsp_types::CompletionItemKind::FUNCTION,
             SymbolKind::Module => lsp_types::CompletionItemKind::MODULE,
             SymbolKind::SelfParam => lsp_types::CompletionItemKind::VALUE,
             SymbolKind::SelfType => lsp_types::CompletionItemKind::TYPE_PARAMETER,
@@ -443,17 +446,24 @@ pub(crate) fn inlay_hint(
     file_id: FileId,
     inlay_hint: InlayHint,
 ) -> Cancellable {
-    let is_visual_studio_code = snap.config.is_visual_studio_code();
     let needs_resolve = inlay_hint.needs_resolve;
     let (label, tooltip, mut something_to_resolve) =
         inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?;
-    let text_edits =
-        if !is_visual_studio_code && needs_resolve && fields_to_resolve.resolve_text_edits {
-            something_to_resolve |= inlay_hint.text_edit.is_some();
-            None
-        } else {
-            inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it))
-        };
+
+    let text_edits = if snap
+        .config
+        .visual_studio_code_version()
+        // https://github.com/microsoft/vscode/issues/193124
+        .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version))
+        && needs_resolve
+        && fields_to_resolve.resolve_text_edits
+    {
+        something_to_resolve |= inlay_hint.text_edit.is_some();
+        None
+    } else {
+        inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it))
+    };
+
     let data = if needs_resolve && something_to_resolve {
         Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap())
     } else {
@@ -667,6 +677,7 @@ fn semantic_token_type_and_modifiers(
             SymbolKind::Trait => semantic_tokens::INTERFACE,
             SymbolKind::TraitAlias => semantic_tokens::INTERFACE,
             SymbolKind::Macro => semantic_tokens::MACRO,
+            SymbolKind::ProcMacro => semantic_tokens::PROC_MACRO,
             SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
             SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
         },
@@ -720,6 +731,7 @@ fn semantic_token_type_and_modifiers(
             HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
             HlMod::Library => semantic_tokens::LIBRARY,
             HlMod::Macro => semantic_tokens::MACRO_MODIFIER,
+            HlMod::ProcMacro => semantic_tokens::PROC_MACRO_MODIFIER,
             HlMod::Mutable => semantic_tokens::MUTABLE,
             HlMod::Public => semantic_tokens::PUBLIC,
             HlMod::Reference => semantic_tokens::REFERENCE,
@@ -1507,13 +1519,28 @@ pub(crate) fn test_item(
         id: test_item.id,
         label: test_item.label,
         kind: match test_item.kind {
-            ide::TestItemKind::Crate => lsp_ext::TestItemKind::Package,
+            ide::TestItemKind::Crate(id) => 'b: {
+                let Some((cargo_ws, target)) = snap.cargo_target_for_crate_root(id) else {
+                    break 'b lsp_ext::TestItemKind::Package;
+                };
+                let target = &cargo_ws[target];
+                match target.kind {
+                    project_model::TargetKind::Bin
+                    | project_model::TargetKind::Lib { .. }
+                    | project_model::TargetKind::Example
+                    | project_model::TargetKind::BuildScript
+                    | project_model::TargetKind::Other => lsp_ext::TestItemKind::Package,
+                    project_model::TargetKind::Test | project_model::TargetKind::Bench => {
+                        lsp_ext::TestItemKind::Test
+                    }
+                }
+            }
             ide::TestItemKind::Module => lsp_ext::TestItemKind::Module,
             ide::TestItemKind::Function => lsp_ext::TestItemKind::Test,
         },
         can_resolve_children: matches!(
             test_item.kind,
-            ide::TestItemKind::Crate | ide::TestItemKind::Module
+            ide::TestItemKind::Crate(_) | ide::TestItemKind::Module
         ),
         parent: test_item.parent,
         text_document: test_item
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index bca6db19dcf9..ffe56e41435c 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -488,20 +488,22 @@ impl GlobalState {
 
     fn update_diagnostics(&mut self) {
         let db = self.analysis_host.raw_database();
-        let subscriptions = self
-            .mem_docs
-            .iter()
-            .map(|path| self.vfs.read().0.file_id(path).unwrap())
-            .filter(|&file_id| {
-                let source_root = db.file_source_root(file_id);
-                // Only publish diagnostics for files in the workspace, not from crates.io deps
-                // or the sysroot.
-                // While theoretically these should never have errors, we have quite a few false
-                // positives particularly in the stdlib, and those diagnostics would stay around
-                // forever if we emitted them here.
-                !db.source_root(source_root).is_library
-            })
-            .collect::>();
+        let subscriptions = {
+            let vfs = &self.vfs.read().0;
+            self.mem_docs
+                .iter()
+                .map(|path| vfs.file_id(path).unwrap())
+                .filter(|&file_id| {
+                    let source_root = db.file_source_root(file_id);
+                    // Only publish diagnostics for files in the workspace, not from crates.io deps
+                    // or the sysroot.
+                    // While theoretically these should never have errors, we have quite a few false
+                    // positives particularly in the stdlib, and those diagnostics would stay around
+                    // forever if we emitted them here.
+                    !db.source_root(source_root).is_library
+                })
+                .collect::>()
+        };
         tracing::trace!("updating notifications for {:?}", subscriptions);
 
         // Diagnostics are triggered by the user typing
@@ -797,6 +799,9 @@ impl GlobalState {
                 self.send_notification::(());
                 self.test_run_session = None;
             }
+            flycheck::CargoTestMessage::Custom { text } => {
+                self.send_notification::(text);
+            }
         }
     }
 
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index dfd25abc70f6..1d831b8b1053 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -171,7 +171,7 @@ impl Project<'_> {
                 ..Default::default()
             },
             roots,
-            false,
+            None,
         );
         config.update(self.config).expect("invalid config");
         config.rediscover_workspaces();
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index 4f6d792201be..e4b0a26a6ff2 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -26,9 +26,19 @@ use salsa::{InternId, InternValue};
 use crate::MacroCallId;
 
 /// Interned [`SyntaxContextData`].
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct SyntaxContextId(InternId);
 
+impl fmt::Debug for SyntaxContextId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if f.alternate() {
+            write!(f, "{}", self.0.as_u32())
+        } else {
+            f.debug_tuple("SyntaxContextId").field(&self.0).finish()
+        }
+    }
+}
+
 impl salsa::InternKey for SyntaxContextId {
     fn from_intern_id(v: salsa::InternId) -> Self {
         SyntaxContextId(v)
diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs
index 0fe3275863d7..6b849ce37381 100644
--- a/crates/span/src/lib.rs
+++ b/crates/span/src/lib.rs
@@ -44,7 +44,10 @@ pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
 
 pub type Span = SpanData;
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+/// Spans represent a region of code, used by the IDE to be able link macro inputs and outputs
+/// together. Positions in spans are relative to some [`SpanAnchor`] to make them more incremental
+/// friendly.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct SpanData {
     /// The text range of this span, relative to the anchor.
     /// We need the anchor for incrementality, as storing absolute ranges will require
@@ -56,9 +59,35 @@ pub struct SpanData {
     pub ctx: Ctx,
 }
 
+impl fmt::Debug for SpanData {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if f.alternate() {
+            fmt::Debug::fmt(&self.anchor.file_id.index(), f)?;
+            f.write_char(':')?;
+            fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
+            f.write_char('@')?;
+            fmt::Debug::fmt(&self.range, f)?;
+            f.write_char('#')?;
+            self.ctx.fmt(f)
+        } else {
+            f.debug_struct("SpanData")
+                .field("range", &self.range)
+                .field("anchor", &self.anchor)
+                .field("ctx", &self.ctx)
+                .finish()
+        }
+    }
+}
+
+impl SpanData {
+    pub fn eq_ignoring_ctx(self, other: Self) -> bool {
+        self.anchor == other.anchor && self.range == other.range
+    }
+}
+
 impl Span {
     #[deprecated = "dummy spans will panic if surfaced incorrectly, as such they should be replaced appropriately"]
-    pub const DUMMY: Self = SpanData {
+    pub const DUMMY: Self = Self {
         range: TextRange::empty(TextSize::new(0)),
         anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID },
         ctx: SyntaxContextId::ROOT,
diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs
index 9f8101c816e5..1f396a1e97b4 100644
--- a/crates/span/src/map.rs
+++ b/crates/span/src/map.rs
@@ -1,23 +1,26 @@
 //! A map that maps a span to every position in a file. Usually maps a span to some range of positions.
 //! Allows bidirectional lookup.
 
-use std::hash::Hash;
+use std::{fmt, hash::Hash};
 
 use stdx::{always, itertools::Itertools};
 use syntax::{TextRange, TextSize};
 use vfs::FileId;
 
-use crate::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
+use crate::{
+    ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID,
+};
 
 /// Maps absolute text ranges for the corresponding file to the relevant span data.
 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
 pub struct SpanMap {
-    spans: Vec<(TextSize, S)>,
-    // FIXME: Should be
-    // spans: Vec<(TextSize, crate::SyntaxContextId)>,
+    spans: Vec<(TextSize, SpanData)>,
 }
 
-impl SpanMap {
+impl SpanMap
+where
+    SpanData: Copy,
+{
     /// Creates a new empty [`SpanMap`].
     pub fn empty() -> Self {
         Self { spans: Vec::new() }
@@ -34,7 +37,7 @@ impl SpanMap {
     }
 
     /// Pushes a new span onto the [`SpanMap`].
-    pub fn push(&mut self, offset: TextSize, span: S) {
+    pub fn push(&mut self, offset: TextSize, span: SpanData) {
         if cfg!(debug_assertions) {
             if let Some(&(last_offset, _)) = self.spans.last() {
                 assert!(
@@ -49,13 +52,31 @@ impl SpanMap {
     /// Returns all [`TextRange`]s that correspond to the given span.
     ///
     /// Note this does a linear search through the entire backing vector.
-    pub fn ranges_with_span(&self, span: S) -> impl Iterator + '_
+    pub fn ranges_with_span_exact(&self, span: SpanData) -> impl Iterator + '_
     where
-        S: Eq,
+        S: Copy,
     {
-        // FIXME: This should ignore the syntax context!
         self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
-            if s != span {
+            if !s.eq_ignoring_ctx(span) {
+                return None;
+            }
+            let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
+            Some(TextRange::new(start, end))
+        })
+    }
+
+    /// Returns all [`TextRange`]s whose spans contain the given span.
+    ///
+    /// Note this does a linear search through the entire backing vector.
+    pub fn ranges_with_span(&self, span: SpanData) -> impl Iterator + '_
+    where
+        S: Copy,
+    {
+        self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
+            if s.anchor != span.anchor {
+                return None;
+            }
+            if !s.range.contains_range(span.range) {
                 return None;
             }
             let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
@@ -64,21 +85,21 @@ impl SpanMap {
     }
 
     /// Returns the span at the given position.
-    pub fn span_at(&self, offset: TextSize) -> S {
+    pub fn span_at(&self, offset: TextSize) -> SpanData {
         let entry = self.spans.partition_point(|&(it, _)| it <= offset);
         self.spans[entry].1
     }
 
     /// Returns the spans associated with the given range.
     /// In other words, this will return all spans that correspond to all offsets within the given range.
-    pub fn spans_for_range(&self, range: TextRange) -> impl Iterator + '_ {
+    pub fn spans_for_range(&self, range: TextRange) -> impl Iterator> + '_ {
         let (start, end) = (range.start(), range.end());
         let start_entry = self.spans.partition_point(|&(it, _)| it <= start);
         let end_entry = self.spans[start_entry..].partition_point(|&(it, _)| it <= end); // FIXME: this might be wrong?
         self.spans[start_entry..][..end_entry].iter().map(|&(_, s)| s)
     }
 
-    pub fn iter(&self) -> impl Iterator + '_ {
+    pub fn iter(&self) -> impl Iterator)> + '_ {
         self.spans.iter().copied()
     }
 }
@@ -92,6 +113,16 @@ pub struct RealSpanMap {
     end: TextSize,
 }
 
+impl fmt::Display for RealSpanMap {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        writeln!(f, "RealSpanMap({:?}):", self.file_id)?;
+        for span in self.pairs.iter() {
+            writeln!(f, "{}: {}", u32::from(span.0), span.1.into_raw().into_u32())?;
+        }
+        Ok(())
+    }
+}
+
 impl RealSpanMap {
     /// Creates a real file span map that returns absolute ranges (relative ranges to the root ast id).
     pub fn absolute(file_id: FileId) -> Self {
diff --git a/crates/stdx/src/anymap.rs b/crates/stdx/src/anymap.rs
index 899cd8ac6bbe..d47b3d1647e9 100644
--- a/crates/stdx/src/anymap.rs
+++ b/crates/stdx/src/anymap.rs
@@ -194,21 +194,6 @@ impl<'a, A: ?Sized + Downcast, V: IntoBox> VacantEntry<'a, A, V> {
 mod tests {
     use super::*;
 
-    #[derive(Clone, Debug, PartialEq)]
-    struct A(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct B(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct C(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct D(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct E(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct F(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct J(i32);
-
     #[test]
     fn test_varieties() {
         fn assert_send() {}
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 1bc1ef8434fc..c3d6f50e6b08 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -139,6 +139,17 @@ impl From for ast::Item {
     }
 }
 
+impl From for ast::Item {
+    fn from(extern_item: ast::ExternItem) -> Self {
+        match extern_item {
+            ast::ExternItem::Static(it) => ast::Item::Static(it),
+            ast::ExternItem::Fn(it) => ast::Item::Fn(it),
+            ast::ExternItem::MacroCall(it) => ast::Item::MacroCall(it),
+            ast::ExternItem::TypeAlias(it) => ast::Item::TypeAlias(it),
+        }
+    }
+}
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum AttrKind {
     Inner,
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index a654366c62a7..8cf65d11c6c0 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -149,12 +149,12 @@ impl ChangeFixture {
         for entry in fixture {
             let text = if entry.text.contains(CURSOR_MARKER) {
                 if entry.text.contains(ESCAPED_CURSOR_MARKER) {
-                    entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER).into()
+                    entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER)
                 } else {
                     let (range_or_offset, text) = extract_range_or_offset(&entry.text);
                     assert!(file_position.is_none());
                     file_position = Some((file_id, range_or_offset));
-                    text.into()
+                    text
                 }
             } else {
                 entry.text.as_str().into()
@@ -251,7 +251,7 @@ impl ChangeFixture {
             fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_owned()));
             roots.push(SourceRoot::new_library(fs));
 
-            source_change.change_file(core_file, Some(mini_core.source_code().into()));
+            source_change.change_file(core_file, Some(mini_core.source_code()));
 
             let all_crates = crate_graph.crates_in_topological_order();
 
@@ -287,7 +287,7 @@ impl ChangeFixture {
             );
             roots.push(SourceRoot::new_library(fs));
 
-            source_change.change_file(proc_lib_file, Some(source.into()));
+            source_change.change_file(proc_lib_file, Some(source));
 
             let all_crates = crate_graph.crates_in_topological_order();
 
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index eec88f80688c..28289a6431e6 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -177,17 +177,19 @@ fn print_debug_subtree(
     let align = "  ".repeat(level);
 
     let Delimiter { kind, open, close } = &subtree.delimiter;
-    let aux = match kind {
-        DelimiterKind::Invisible => format!("$$ {:?} {:?}", open, close),
-        DelimiterKind::Parenthesis => format!("() {:?} {:?}", open, close),
-        DelimiterKind::Brace => format!("{{}} {:?} {:?}", open, close),
-        DelimiterKind::Bracket => format!("[] {:?} {:?}", open, close),
+    let delim = match kind {
+        DelimiterKind::Invisible => "$$",
+        DelimiterKind::Parenthesis => "()",
+        DelimiterKind::Brace => "{}",
+        DelimiterKind::Bracket => "[]",
     };
 
-    if subtree.token_trees.is_empty() {
-        write!(f, "{align}SUBTREE {aux}")?;
-    } else {
-        writeln!(f, "{align}SUBTREE {aux}")?;
+    write!(f, "{align}SUBTREE {delim} ",)?;
+    fmt::Debug::fmt(&open, f)?;
+    write!(f, " ")?;
+    fmt::Debug::fmt(&close, f)?;
+    if !subtree.token_trees.is_empty() {
+        writeln!(f)?;
         for (idx, child) in subtree.token_trees.iter().enumerate() {
             print_debug_token(f, child, level + 1)?;
             if idx != subtree.token_trees.len() - 1 {
@@ -208,16 +210,24 @@ fn print_debug_token(
 
     match tkn {
         TokenTree::Leaf(leaf) => match leaf {
-            Leaf::Literal(lit) => write!(f, "{}LITERAL {} {:?}", align, lit.text, lit.span)?,
-            Leaf::Punct(punct) => write!(
-                f,
-                "{}PUNCH   {} [{}] {:?}",
-                align,
-                punct.char,
-                if punct.spacing == Spacing::Alone { "alone" } else { "joint" },
-                punct.span
-            )?,
-            Leaf::Ident(ident) => write!(f, "{}IDENT   {} {:?}", align, ident.text, ident.span)?,
+            Leaf::Literal(lit) => {
+                write!(f, "{}LITERAL {}", align, lit.text)?;
+                fmt::Debug::fmt(&lit.span, f)?;
+            }
+            Leaf::Punct(punct) => {
+                write!(
+                    f,
+                    "{}PUNCH   {} [{}] ",
+                    align,
+                    punct.char,
+                    if punct.spacing == Spacing::Alone { "alone" } else { "joint" },
+                )?;
+                fmt::Debug::fmt(&punct.span, f)?;
+            }
+            Leaf::Ident(ident) => {
+                write!(f, "{}IDENT   {} ", align, ident.text)?;
+                fmt::Debug::fmt(&ident.span, f)?;
+            }
         },
         TokenTree::Subtree(subtree) => {
             print_debug_subtree(f, subtree, level)?;
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index af5b4e51ef37..cf9ad5fe04dd 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
  $DIR/strict_provenance_cast.rs:LL:CC
    |
-LL |     let _ptr = std::ptr::from_exposed_addr::(addr);
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer casts and `ptr::from_exposed_addr` are not supported with `-Zmiri-strict-provenance`
+LL |     let _ptr = std::ptr::with_exposed_provenance::(addr);
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance`
    |
    = help: use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead
    = note: BACKTRACE:
diff --git a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
index b0e4cceb98f3..aa05649d550c 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
@@ -7,6 +7,6 @@ fn main() {
     let mut x = 0;
     let _fool = &mut x as *mut i32; // this would have fooled the old untagged pointer logic
     let addr = (&x as *const i32).expose_addr();
-    let ptr = std::ptr::from_exposed_addr_mut::(addr);
+    let ptr = std::ptr::with_exposed_provenance_mut::(addr);
     unsafe { *ptr = 0 }; //~ ERROR: /write access using  .* no exposed tags have suitable permission in the borrow stack/
 }
diff --git a/src/tools/miri/tests/pass/box.stack.stderr b/src/tools/miri/tests/pass/box.stack.stderr
index f6e208cea9a8..1a4d52ee3146 100644
--- a/src/tools/miri/tests/pass/box.stack.stderr
+++ b/src/tools/miri/tests/pass/box.stack.stderr
@@ -4,11 +4,11 @@ warning: integer-to-pointer cast
 LL |         let r2 = ((r as usize) + 0) as *mut i32;
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
    |
-   = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,
+   = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`,
    = help: which means that Miri might miss pointer bugs in this program.
-   = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation.
+   = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation.
    = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.
-   = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics.
+   = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics.
    = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.
    = note: BACKTRACE:
    = note: inside `into_raw` at $DIR/box.rs:LL:CC
diff --git a/src/tools/miri/tests/pass/extern_types.stack.stderr b/src/tools/miri/tests/pass/extern_types.stack.stderr
index 2e18f6930589..275d718129b4 100644
--- a/src/tools/miri/tests/pass/extern_types.stack.stderr
+++ b/src/tools/miri/tests/pass/extern_types.stack.stderr
@@ -4,11 +4,11 @@ warning: integer-to-pointer cast
 LL |     let x: &Foo = unsafe { &*(16 as *const Foo) };
    |                              ^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
    |
-   = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,
+   = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`,
    = help: which means that Miri might miss pointer bugs in this program.
-   = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation.
+   = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation.
    = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.
-   = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics.
+   = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics.
    = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.
    = note: BACKTRACE:
    = note: inside `main` at $DIR/extern_types.rs:LL:CC
diff --git a/src/tools/miri/tests/pass/portable-simd-ptrs.rs b/src/tools/miri/tests/pass/portable-simd-ptrs.rs
index 70ba5636c600..3b2d221bd8ea 100644
--- a/src/tools/miri/tests/pass/portable-simd-ptrs.rs
+++ b/src/tools/miri/tests/pass/portable-simd-ptrs.rs
@@ -8,5 +8,5 @@ fn main() {
     // Pointer casts
     let _val: Simd<*const u8, 4> = Simd::<*const i32, 4>::splat(ptr::null()).cast();
     let addrs = Simd::<*const i32, 4>::splat(ptr::null()).expose_addr();
-    let _ptrs = Simd::<*const i32, 4>::from_exposed_addr(addrs);
+    let _ptrs = Simd::<*const i32, 4>::with_exposed_provenance(addrs);
 }
diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
index d8d57679e6b3..8555de986f06 100644
--- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
+++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
@@ -12,7 +12,7 @@ fn ptr_roundtrip_out_of_bounds() {
 
     let x_usize = x_ptr.wrapping_offset(128).expose_addr();
 
-    let ptr = ptr::from_exposed_addr::(x_usize).wrapping_offset(-128);
+    let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128);
     assert_eq!(unsafe { *ptr }, 3);
 }
 
@@ -27,7 +27,7 @@ fn ptr_roundtrip_confusion() {
     let x_usize = x_ptr.expose_addr();
     let y_usize = y_ptr.expose_addr();
 
-    let ptr = ptr::from_exposed_addr::(y_usize);
+    let ptr = ptr::with_exposed_provenance::(y_usize);
     let ptr = ptr.with_addr(x_usize);
     assert_eq!(unsafe { *ptr }, 0);
 }
@@ -39,7 +39,7 @@ fn ptr_roundtrip_imperfect() {
 
     let x_usize = x_ptr.expose_addr() + 128;
 
-    let ptr = ptr::from_exposed_addr::(x_usize).wrapping_offset(-128);
+    let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128);
     assert_eq!(unsafe { *ptr }, 3);
 }
 
@@ -51,7 +51,7 @@ fn ptr_roundtrip_null() {
     let null = x_null_ptr.expose_addr();
     assert_eq!(null, 0);
 
-    let x_null_ptr_copy = ptr::from_exposed_addr::(null); // just a roundtrip, so has provenance of x (angelically)
+    let x_null_ptr_copy = ptr::with_exposed_provenance::(null); // just a roundtrip, so has provenance of x (angelically)
     let x_ptr_copy = x_null_ptr_copy.with_addr(x_ptr.addr()); // addr of x and provenance of x
     assert_eq!(unsafe { *x_ptr_copy }, 42);
 }
diff --git a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
index e467356dd04b..5622bf186545 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
@@ -39,7 +39,7 @@ fn example(variant: bool) {
         // 4 is the "obvious" choice (topmost tag, what we used to do with untagged pointers).
         // And indeed if `variant == true` it is the only possible choice.
         // But if `variant == false` then 2 is the only possible choice!
-        let x_wildcard = ptr::from_exposed_addr_mut::(x_raw2_addr);
+        let x_wildcard = ptr::with_exposed_provenance_mut::(x_raw2_addr);
 
         if variant {
             // If we picked 2, this will invalidate 3.
diff --git a/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr b/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr
index f3ba052ae513..7cbfad3942b3 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr
+++ b/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr
@@ -4,11 +4,11 @@ warning: integer-to-pointer cast
 LL |         let wildcard = &root0 as *const Cell as usize as *const Cell;
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
    |
-   = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,
+   = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`,
    = help: which means that Miri might miss pointer bugs in this program.
-   = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation.
+   = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation.
    = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.
-   = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics.
+   = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics.
    = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.
    = note: BACKTRACE:
    = note: inside `main` at $DIR/issue-miri-2389.rs:LL:CC
diff --git a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
index 5bb4e879c3e4..6e177a6e4abb 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
@@ -9,7 +9,7 @@ fn main() {
 
     // Expose the allocation and use the exposed pointer, creating an unknown bottom
     unsafe {
-        let p: *mut u8 = ptr::from_exposed_addr::(ptr.expose_addr()) as *mut u8;
+        let p: *mut u8 = ptr::with_exposed_provenance::(ptr.expose_addr()) as *mut u8;
         *p = 1;
     }
 
diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr
index 0b1289742758..24f2500abf82 100644
--- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr
+++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr
@@ -4,7 +4,7 @@ error: strict provenance disallows casting integer `usize` to pointer `*const u8
 LL |     let dangling = 16_usize as *const u8;
    |                    ^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead
+   = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead
 note: the lint level is defined here
   --> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9
    |
diff --git a/tests/ui/simd/intrinsic/ptr-cast.rs b/tests/ui/simd/intrinsic/ptr-cast.rs
index 83d86baf334a..40c782c153eb 100644
--- a/tests/ui/simd/intrinsic/ptr-cast.rs
+++ b/tests/ui/simd/intrinsic/ptr-cast.rs
@@ -24,10 +24,10 @@ fn main() {
 
         let exposed_addr: V = simd_expose_addr(const_ptrs);
 
-        let from_exposed_addr: V<*mut i8> = simd_from_exposed_addr(exposed_addr);
+        let with_exposed_provenance: V<*mut i8> = simd_from_exposed_addr(exposed_addr);
 
         assert!(const_ptrs.0 == [ptr as *const u8, core::ptr::null()]);
         assert!(exposed_addr.0 == [ptr as usize, 0]);
-        assert!(from_exposed_addr.0 == ptrs.0);
+        assert!(with_exposed_provenance.0 == ptrs.0);
     }
 }

From 038e7c6c38b9e1713fb258c783441bb0102b8492 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 23 Mar 2024 12:21:20 +0100
Subject: [PATCH 084/192] rename MIR int2ptr casts to match library name

---
 compiler/rustc_borrowck/src/type_check/mod.rs                 | 4 ++--
 compiler/rustc_codegen_cranelift/src/base.rs                  | 2 +-
 compiler/rustc_codegen_ssa/src/mir/rvalue.rs                  | 2 +-
 compiler/rustc_const_eval/src/interpret/cast.rs               | 2 +-
 compiler/rustc_const_eval/src/transform/check_consts/check.rs | 2 +-
 compiler/rustc_const_eval/src/transform/validate.rs           | 2 +-
 compiler/rustc_middle/src/mir/statement.rs                    | 2 +-
 compiler/rustc_middle/src/mir/syntax.rs                       | 2 +-
 compiler/rustc_middle/src/ty/cast.rs                          | 2 +-
 compiler/rustc_smir/src/rustc_smir/convert/mir.rs             | 2 +-
 compiler/stable_mir/src/mir/body.rs                           | 2 +-
 src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs     | 2 +-
 .../building/custom/as_cast.int_to_ptr.built.after.mir        | 2 +-
 tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff           | 2 +-
 tests/mir-opt/const_prop/reify_fn_ptr.rs                      | 2 +-
 15 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 700b5e13dec9..60596ca58d88 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -2263,7 +2263,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
 
-                    CastKind::PointerFromExposedAddress => {
+                    CastKind::PointerWithExposedProvenance => {
                         let ty_from = op.ty(body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
@@ -2273,7 +2273,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 span_mirbug!(
                                     self,
                                     rvalue,
-                                    "Invalid PointerFromExposedAddress cast {:?} -> {:?}",
+                                    "Invalid PointerWithExposedProvenance cast {:?} -> {:?}",
                                     ty_from,
                                     ty
                                 )
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 047dc56a32ea..e7823013260b 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -642,7 +642,7 @@ fn codegen_stmt<'tcx>(
                     | CastKind::FnPtrToPtr
                     | CastKind::PtrToPtr
                     | CastKind::PointerExposeAddress
-                    | CastKind::PointerFromExposedAddress,
+                    | CastKind::PointerWithExposedProvenance,
                     ref operand,
                     to_ty,
                 ) => {
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 15f2e0e56d86..af6ea596a218 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -508,7 +508,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     // Since int2ptr can have arbitrary integer types as input (so we have to do
                     // sign extension and all that), it is currently best handled in the same code
                     // path as the other integer-to-X casts.
-                    | mir::CastKind::PointerFromExposedAddress => {
+                    | mir::CastKind::PointerWithExposedProvenance => {
                         assert!(bx.cx().is_backend_immediate(cast));
                         let ll_t_out = bx.cx().immediate_backend_type(cast);
                         if operand.layout.abi.is_uninhabited() {
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index bbf11f169f98..3b2b54eb8396 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -40,7 +40,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 self.write_immediate(*res, dest)?;
             }
 
-            CastKind::PointerFromExposedAddress => {
+            CastKind::PointerWithExposedProvenance => {
                 let src = self.read_immediate(src)?;
                 let res = self.pointer_from_exposed_address_cast(&src, cast_layout)?;
                 self.write_immediate(*res, dest)?;
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index a93e8138aa41..51dd556bbb96 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -547,7 +547,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
             Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => {
                 self.check_op(ops::RawPtrToIntCast);
             }
-            Rvalue::Cast(CastKind::PointerFromExposedAddress, _, _) => {
+            Rvalue::Cast(CastKind::PointerWithExposedProvenance, _, _) => {
                 // Since no pointer can ever get exposed (rejected above), this is easy to support.
             }
 
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 4bc49f906070..bff8db66f683 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -1057,7 +1057,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         // FIXME(dyn-star): make sure nothing needs to be done here.
                     }
                     // FIXME: Add Checks for these
-                    CastKind::PointerFromExposedAddress
+                    CastKind::PointerWithExposedProvenance
                     | CastKind::PointerExposeAddress
                     | CastKind::PointerCoercion(_) => {}
                     CastKind::IntToInt | CastKind::IntToFloat => {
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
index f929a5cec253..947764307e49 100644
--- a/compiler/rustc_middle/src/mir/statement.rs
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -426,7 +426,7 @@ impl<'tcx> Rvalue<'tcx> {
                 | CastKind::FnPtrToPtr
                 | CastKind::PtrToPtr
                 | CastKind::PointerCoercion(_)
-                | CastKind::PointerFromExposedAddress
+                | CastKind::PointerWithExposedProvenance
                 | CastKind::DynStar
                 | CastKind::Transmute,
                 _,
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index a9dd59f0f3e3..a9689d08ddcf 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1319,7 +1319,7 @@ pub enum CastKind {
     PointerExposeAddress,
     /// An address-to-pointer cast that picks up an exposed provenance.
     /// See the docs on `with_exposed_provenance` for more details.
-    PointerFromExposedAddress,
+    PointerWithExposedProvenance,
     /// Pointer related casts that are done by coercions. Note that reference-to-raw-ptr casts are
     /// translated into `&raw mut/const *r`, i.e., they are not actually casts.
     PointerCoercion(PointerCoercion),
diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs
index 50d629120ab5..9bdc679d4e5b 100644
--- a/compiler/rustc_middle/src/ty/cast.rs
+++ b/compiler/rustc_middle/src/ty/cast.rs
@@ -85,7 +85,7 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin
         (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
             mir::CastKind::PointerExposeAddress
         }
-        (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerFromExposedAddress,
+        (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance,
         (_, Some(CastTy::DynStar)) => mir::CastKind::DynStar,
         (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => mir::CastKind::IntToInt,
         (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => mir::CastKind::FnPtrToPtr,
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index c0876adf9050..571de5f4fbfa 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -274,7 +274,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind {
         use rustc_middle::mir::CastKind::*;
         match self {
             PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress,
-            PointerFromExposedAddress => stable_mir::mir::CastKind::PointerFromExposedAddress,
+            PointerWithExposedProvenance => stable_mir::mir::CastKind::PointerWithExposedProvenance,
             PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)),
             DynStar => stable_mir::mir::CastKind::DynStar,
             IntToInt => stable_mir::mir::CastKind::IntToInt,
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index e4a012d8c477..86c2543aea31 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -968,7 +968,7 @@ pub enum PointerCoercion {
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum CastKind {
     PointerExposeAddress,
-    PointerFromExposedAddress,
+    PointerWithExposedProvenance,
     PointerCoercion(PointerCoercion),
     DynStar,
     IntToInt,
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index dadb0d662ce8..0c4260037f23 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -112,7 +112,7 @@ fn check_rvalue<'tcx>(
         Rvalue::Repeat(operand, _)
         | Rvalue::Use(operand)
         | Rvalue::Cast(
-            CastKind::PointerFromExposedAddress
+            CastKind::PointerWithExposedProvenance
             | CastKind::IntToInt
             | CastKind::FloatToInt
             | CastKind::IntToFloat
diff --git a/tests/mir-opt/building/custom/as_cast.int_to_ptr.built.after.mir b/tests/mir-opt/building/custom/as_cast.int_to_ptr.built.after.mir
index 0dc46d61effb..faff79e8c578 100644
--- a/tests/mir-opt/building/custom/as_cast.int_to_ptr.built.after.mir
+++ b/tests/mir-opt/building/custom/as_cast.int_to_ptr.built.after.mir
@@ -4,7 +4,7 @@ fn int_to_ptr(_1: usize) -> *const i32 {
     let mut _0: *const i32;
 
     bb0: {
-        _0 = _1 as *const i32 (PointerFromExposedAddress);
+        _0 = _1 as *const i32 (PointerWithExposedProvenance);
         return;
     }
 }
diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff b/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff
index cde0cb32f756..2f76e74a3f87 100644
--- a/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff
+++ b/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff
@@ -16,7 +16,7 @@
           _3 = main as fn() (PointerCoercion(ReifyFnPointer));
           _2 = move _3 as usize (PointerExposeAddress);
           StorageDead(_3);
-          _1 = move _2 as *const fn() (PointerFromExposedAddress);
+          _1 = move _2 as *const fn() (PointerWithExposedProvenance);
           StorageDead(_2);
           StorageDead(_1);
           _0 = const ();
diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.rs b/tests/mir-opt/const_prop/reify_fn_ptr.rs
index ad73b0842195..4e897d227680 100644
--- a/tests/mir-opt/const_prop/reify_fn_ptr.rs
+++ b/tests/mir-opt/const_prop/reify_fn_ptr.rs
@@ -5,6 +5,6 @@ fn main() {
     // CHECK-LABEL: fn main(
     // CHECK: [[ptr:_.*]] = main as fn() (PointerCoercion(ReifyFnPointer));
     // CHECK: [[addr:_.*]] = move [[ptr]] as usize (PointerExposeAddress);
-    // CHECK: [[back:_.*]] = move [[addr]] as *const fn() (PointerFromExposedAddress);
+    // CHECK: [[back:_.*]] = move [[addr]] as *const fn() (PointerWithExposedProvenance);
     let _ = main as usize as *const fn();
 }

From 5afe4a9e09b09b2a74cf5cbce5b69ca40b3873a9 Mon Sep 17 00:00:00 2001
From: Andy Kurnia 
Date: Sat, 23 Mar 2024 21:38:32 +0800
Subject: [PATCH 085/192] improve example on inserting to a sorted vector to
 avoid shifting equal elements

---
 library/alloc/src/collections/vec_deque/mod.rs | 2 +-
 library/core/src/slice/mod.rs                  | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 41adc2e79dc7..41935a694209 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -2464,7 +2464,7 @@ impl VecDeque {
     ///
     /// let mut deque: VecDeque<_> = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
     /// let num = 42;
-    /// let idx = deque.partition_point(|&x| x < num);
+    /// let idx = deque.partition_point(|&x| x <= num);
     /// // The above is equivalent to `let idx = deque.binary_search(&num).unwrap_or_else(|x| x);`
     /// deque.insert(idx, num);
     /// assert_eq!(deque, &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 4a574bf03474..b1ae07b05f7e 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2728,7 +2728,7 @@ impl [T] {
     /// ```
     /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
     /// let num = 42;
-    /// let idx = s.partition_point(|&x| x < num);
+    /// let idx = s.partition_point(|&x| x <= num);
     /// // The above is equivalent to `let idx = s.binary_search(&num).unwrap_or_else(|x| x);`
     /// s.insert(idx, num);
     /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
@@ -4179,7 +4179,7 @@ impl [T] {
     /// ```
     /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
     /// let num = 42;
-    /// let idx = s.partition_point(|&x| x < num);
+    /// let idx = s.partition_point(|&x| x <= num);
     /// s.insert(idx, num);
     /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
     /// ```

From f2cff5ebb9ebc9712eec88dd5ec578f76efb33bc Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 23 Mar 2024 23:00:53 +0100
Subject: [PATCH 086/192] also rename the SIMD intrinsic

---
 compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs    | 2 +-
 compiler/rustc_codegen_llvm/src/intrinsic.rs               | 2 +-
 compiler/rustc_const_eval/src/interpret/cast.rs            | 4 ++--
 compiler/rustc_hir_analysis/src/check/intrinsic.rs         | 2 +-
 compiler/rustc_span/src/symbol.rs                          | 2 +-
 library/core/src/intrinsics/simd.rs                        | 7 +++++++
 .../crates/core_simd/src/simd/ptr/const_ptr.rs             | 2 +-
 .../portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs | 2 +-
 src/tools/miri/src/shims/intrinsics/simd.rs                | 6 +++---
 tests/ui/simd/intrinsic/ptr-cast.rs                        | 4 ++--
 10 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index 4d55a95aa9db..783ad5d1dd1f 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -965,7 +965,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
             });
         }
 
-        sym::simd_expose_addr | sym::simd_from_exposed_addr | sym::simd_cast_ptr => {
+        sym::simd_expose_addr | sym::simd_with_exposed_provenance | sym::simd_cast_ptr => {
             intrinsic_args!(fx, args => (arg); intrinsic);
             ret.write_cvalue_transmute(fx, arg);
         }
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 2409b2e78d73..c7067093c581 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -2133,7 +2133,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         return Ok(bx.ptrtoint(args[0].immediate(), llret_ty));
     }
 
-    if name == sym::simd_from_exposed_addr {
+    if name == sym::simd_with_exposed_provenance {
         let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
         require!(
             in_len == out_len,
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 3b2b54eb8396..e0d45f1fe114 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -42,7 +42,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
             CastKind::PointerWithExposedProvenance => {
                 let src = self.read_immediate(src)?;
-                let res = self.pointer_from_exposed_address_cast(&src, cast_layout)?;
+                let res = self.pointer_with_exposed_provenance_cast(&src, cast_layout)?;
                 self.write_immediate(*res, dest)?;
             }
 
@@ -242,7 +242,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         Ok(ImmTy::from_scalar(self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, cast_to))
     }
 
-    pub fn pointer_from_exposed_address_cast(
+    pub fn pointer_with_exposed_provenance_cast(
         &self,
         src: &ImmTy<'tcx, M::Provenance>,
         cast_to: TyAndLayout<'tcx>,
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 0b526a8c9775..fc551693dd14 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -623,7 +623,7 @@ pub fn check_intrinsic_type(
             | sym::simd_as
             | sym::simd_cast_ptr
             | sym::simd_expose_addr
-            | sym::simd_from_exposed_addr => (2, 0, vec![param(0)], param(1)),
+            | sym::simd_with_exposed_provenance => (2, 0, vec![param(0)], param(1)),
             sym::simd_bitmask => (2, 0, vec![param(0)], param(1)),
             sym::simd_select | sym::simd_select_bitmask => {
                 (2, 0, vec![param(0), param(1), param(1)], param(1))
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index b6c07e8737fc..34743d53133a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1649,7 +1649,6 @@ symbols! {
         simd_fmin,
         simd_fpow,
         simd_fpowi,
-        simd_from_exposed_addr,
         simd_fsin,
         simd_fsqrt,
         simd_gather,
@@ -1688,6 +1687,7 @@ symbols! {
         simd_shuffle_generic,
         simd_sub,
         simd_trunc,
+        simd_with_exposed_provenance,
         simd_xor,
         since,
         sinf128,
diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs
index b69f4f853b99..f28ba84f7e95 100644
--- a/library/core/src/intrinsics/simd.rs
+++ b/library/core/src/intrinsics/simd.rs
@@ -549,6 +549,10 @@ extern "rust-intrinsic" {
     ///
     /// `U` must be a vector of pointers, with the same length as `T`.
     #[rustc_nounwind]
+    #[cfg(not(bootstrap))]
+    pub fn simd_with_exposed_provenance(addr: T) -> U;
+    #[rustc_nounwind]
+    #[cfg(bootstrap)]
     pub fn simd_from_exposed_addr(addr: T) -> U;
 
     /// Swap bytes of each element.
@@ -655,3 +659,6 @@ extern "rust-intrinsic" {
     #[rustc_nounwind]
     pub fn simd_flog(a: T) -> T;
 }
+
+#[cfg(bootstrap)]
+pub use simd_from_exposed_addr as simd_with_exposed_provenance;
diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs
index 3ec9fccbff95..4d2fe999ca6f 100644
--- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs
+++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs
@@ -139,7 +139,7 @@ where
     #[inline]
     fn with_exposed_provenance(addr: Self::Usize) -> Self {
         // Safety: `self` is a pointer vector
-        unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) }
+        unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) }
     }
 
     #[inline]
diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs
index 1142839e213b..b3437b9c4996 100644
--- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs
+++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs
@@ -136,7 +136,7 @@ where
     #[inline]
     fn with_exposed_provenance(addr: Self::Usize) -> Self {
         // Safety: `self` is a pointer vector
-        unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) }
+        unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) }
     }
 
     #[inline]
diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs
index c97a052f5171..d10444b70ef9 100644
--- a/src/tools/miri/src/shims/intrinsics/simd.rs
+++ b/src/tools/miri/src/shims/intrinsics/simd.rs
@@ -484,7 +484,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?;
                 this.write_int(res, &dest)?;
             }
-            "cast" | "as" | "cast_ptr" | "expose_addr" | "from_exposed_addr" => {
+            "cast" | "as" | "cast_ptr" | "expose_addr" | "with_exposed_provenance" => {
                 let [op] = check_arg_count(args)?;
                 let (op, op_len) = this.operand_to_simd(op)?;
                 let (dest, dest_len) = this.mplace_to_simd(dest)?;
@@ -495,7 +495,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let safe_cast = intrinsic_name == "as";
                 let ptr_cast = intrinsic_name == "cast_ptr";
                 let expose_cast = intrinsic_name == "expose_addr";
-                let from_exposed_cast = intrinsic_name == "from_exposed_addr";
+                let from_exposed_cast = intrinsic_name == "with_exposed_provenance";
 
                 for i in 0..dest_len {
                     let op = this.read_immediate(&this.project_index(&op, i)?)?;
@@ -529,7 +529,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                         (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) if expose_cast =>
                             this.pointer_expose_address_cast(&op, dest.layout)?,
                         (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast =>
-                            this.pointer_from_exposed_address_cast(&op, dest.layout)?,
+                            this.pointer_with_exposed_provenance_cast(&op, dest.layout)?,
                         // Error otherwise
                         _ =>
                             throw_unsup_format!(
diff --git a/tests/ui/simd/intrinsic/ptr-cast.rs b/tests/ui/simd/intrinsic/ptr-cast.rs
index 40c782c153eb..628203462414 100644
--- a/tests/ui/simd/intrinsic/ptr-cast.rs
+++ b/tests/ui/simd/intrinsic/ptr-cast.rs
@@ -5,7 +5,7 @@
 extern "rust-intrinsic" {
     fn simd_cast_ptr(x: T) -> U;
     fn simd_expose_addr(x: T) -> U;
-    fn simd_from_exposed_addr(x: T) -> U;
+    fn simd_with_exposed_provenance(x: T) -> U;
 }
 
 #[derive(Copy, Clone)]
@@ -24,7 +24,7 @@ fn main() {
 
         let exposed_addr: V = simd_expose_addr(const_ptrs);
 
-        let with_exposed_provenance: V<*mut i8> = simd_from_exposed_addr(exposed_addr);
+        let with_exposed_provenance: V<*mut i8> = simd_with_exposed_provenance(exposed_addr);
 
         assert!(const_ptrs.0 == [ptr as *const u8, core::ptr::null()]);
         assert!(exposed_addr.0 == [ptr as usize, 0]);

From 643029693b2987b4b7c8b5073e8a25536cca7069 Mon Sep 17 00:00:00 2001
From: Andy Kurnia 
Date: Sun, 24 Mar 2024 08:15:00 +0800
Subject: [PATCH 087/192] clarify equivalency of binary_search and
 partition_point

---
 library/alloc/src/collections/vec_deque/mod.rs | 4 +++-
 library/core/src/slice/mod.rs                  | 4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 41935a694209..b1752a3465fe 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -2465,7 +2465,9 @@ impl VecDeque {
     /// let mut deque: VecDeque<_> = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
     /// let num = 42;
     /// let idx = deque.partition_point(|&x| x <= num);
-    /// // The above is equivalent to `let idx = deque.binary_search(&num).unwrap_or_else(|x| x);`
+    /// // If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to
+    /// // `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` may allow `insert`
+    /// // to shift less elements.
     /// deque.insert(idx, num);
     /// assert_eq!(deque, &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
     /// ```
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index b1ae07b05f7e..d30ede7d1f94 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2729,7 +2729,9 @@ impl [T] {
     /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
     /// let num = 42;
     /// let idx = s.partition_point(|&x| x <= num);
-    /// // The above is equivalent to `let idx = s.binary_search(&num).unwrap_or_else(|x| x);`
+    /// // If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to
+    /// // `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` will allow `insert`
+    /// // to shift less elements.
     /// s.insert(idx, num);
     /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
     /// ```

From 744c664ba2e6440024457d5ec0d3600b3e0c0144 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sun, 26 Nov 2023 00:36:56 -0800
Subject: [PATCH 088/192] Add a MIR pre-codegen test for derived PartialOrd

---
 tests/mir-opt/pre-codegen/derived_ord.rs      |   9 +
 ....{impl#0}-partial_cmp.PreCodegen.after.mir | 159 ++++++++++++++++++
 2 files changed, 168 insertions(+)
 create mode 100644 tests/mir-opt/pre-codegen/derived_ord.rs
 create mode 100644 tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir

diff --git a/tests/mir-opt/pre-codegen/derived_ord.rs b/tests/mir-opt/pre-codegen/derived_ord.rs
new file mode 100644
index 000000000000..bad751edf841
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/derived_ord.rs
@@ -0,0 +1,9 @@
+// skip-filecheck
+//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0
+
+#![crate_type = "lib"]
+
+#[derive(PartialOrd, PartialEq)]
+pub struct MultiField(char, i16);
+
+// EMIT_MIR derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
diff --git a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
new file mode 100644
index 000000000000..dd7fce3ed0b4
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
@@ -0,0 +1,159 @@
+// MIR for `::partial_cmp` after PreCodegen
+
+fn ::partial_cmp(_1: &MultiField, _2: &MultiField) -> Option {
+    debug self => _1;
+    debug other => _2;
+    let mut _0: std::option::Option;
+    let mut _3: &char;
+    let mut _4: &char;
+    let mut _10: std::option::Option;
+    let mut _11: &i16;
+    let mut _12: &i16;
+    let _18: std::option::Option;
+    scope 1 {
+        debug cmp => _18;
+    }
+    scope 2 (inlined std::cmp::impls::::partial_cmp) {
+        debug self => _3;
+        debug other => _4;
+        let mut _9: std::cmp::Ordering;
+        scope 3 (inlined std::cmp::impls::::cmp) {
+            debug self => _3;
+            debug other => _4;
+            let mut _5: char;
+            let mut _6: char;
+            let mut _7: bool;
+            let mut _8: bool;
+        }
+    }
+    scope 4 (inlined std::cmp::impls::::partial_cmp) {
+        debug self => _11;
+        debug other => _12;
+        let mut _17: std::cmp::Ordering;
+        scope 5 (inlined std::cmp::impls::::cmp) {
+            debug self => _11;
+            debug other => _12;
+            let mut _13: i16;
+            let mut _14: i16;
+            let mut _15: bool;
+            let mut _16: bool;
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = &((*_1).0: char);
+        StorageLive(_4);
+        _4 = &((*_2).0: char);
+        StorageLive(_9);
+        StorageLive(_5);
+        StorageLive(_6);
+        StorageLive(_7);
+        _5 = ((*_1).0: char);
+        _6 = ((*_2).0: char);
+        _7 = Lt(_5, _6);
+        switchInt(move _7) -> [0: bb1, otherwise: bb10];
+    }
+
+    bb1: {
+        StorageLive(_8);
+        _8 = Eq(_5, _6);
+        switchInt(move _8) -> [0: bb2, otherwise: bb3];
+    }
+
+    bb2: {
+        _9 = const Greater;
+        StorageDead(_8);
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_5);
+        _10 = Option::::Some(move _9);
+        StorageDead(_9);
+        StorageDead(_4);
+        StorageDead(_3);
+        goto -> bb11;
+    }
+
+    bb3: {
+        StorageDead(_8);
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_5);
+        StorageDead(_9);
+        StorageDead(_4);
+        StorageDead(_3);
+        StorageLive(_11);
+        _11 = &((*_1).1: i16);
+        StorageLive(_12);
+        _12 = &((*_2).1: i16);
+        StorageLive(_17);
+        StorageLive(_13);
+        StorageLive(_14);
+        StorageLive(_15);
+        _13 = ((*_1).1: i16);
+        _14 = ((*_2).1: i16);
+        _15 = Lt(_13, _14);
+        switchInt(move _15) -> [0: bb4, otherwise: bb8];
+    }
+
+    bb4: {
+        StorageLive(_16);
+        _16 = Eq(_13, _14);
+        switchInt(move _16) -> [0: bb5, otherwise: bb6];
+    }
+
+    bb5: {
+        _17 = const Greater;
+        goto -> bb7;
+    }
+
+    bb6: {
+        _17 = const Equal;
+        goto -> bb7;
+    }
+
+    bb7: {
+        StorageDead(_16);
+        goto -> bb9;
+    }
+
+    bb8: {
+        _17 = const Less;
+        goto -> bb9;
+    }
+
+    bb9: {
+        StorageDead(_15);
+        StorageDead(_14);
+        StorageDead(_13);
+        _0 = Option::::Some(move _17);
+        StorageDead(_17);
+        StorageDead(_12);
+        StorageDead(_11);
+        goto -> bb12;
+    }
+
+    bb10: {
+        _9 = const Less;
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_5);
+        _10 = Option::::Some(move _9);
+        StorageDead(_9);
+        StorageDead(_4);
+        StorageDead(_3);
+        goto -> bb11;
+    }
+
+    bb11: {
+        StorageLive(_18);
+        _18 = _10;
+        _0 = _10;
+        StorageDead(_18);
+        goto -> bb12;
+    }
+
+    bb12: {
+        return;
+    }
+}

From 3da115a93b782796e3d267266b4241e5258f1fef Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sun, 5 Mar 2023 20:19:41 -0800
Subject: [PATCH 089/192] Add+Use `mir::BinOp::Cmp`

---
 .../src/codegen_i128.rs                       |   3 +-
 compiler/rustc_codegen_cranelift/src/num.rs   |  24 ++-
 compiler/rustc_codegen_gcc/src/common.rs      |   4 +
 compiler/rustc_codegen_llvm/src/common.rs     |   4 +
 compiler/rustc_codegen_ssa/src/mir/rvalue.rs  |  30 ++++
 .../rustc_codegen_ssa/src/traits/consts.rs    |   1 +
 .../rustc_const_eval/src/interpret/operand.rs |   7 +
 .../src/interpret/operator.rs                 |  18 +++
 .../src/transform/validate.rs                 |   9 ++
 compiler/rustc_const_eval/src/util/mod.rs     |   4 +-
 compiler/rustc_hir/src/lang_items.rs          |   1 +
 .../rustc_hir_analysis/src/check/intrinsic.rs |   5 +
 compiler/rustc_middle/src/mir/syntax.rs       |   2 +
 compiler/rustc_middle/src/mir/tcx.rs          |   8 +-
 compiler/rustc_middle/src/ty/context.rs       |   7 +
 .../src/lower_intrinsics.rs                   |   2 +
 .../rustc_mir_transform/src/promote_consts.rs |   1 +
 .../rustc_smir/src/rustc_smir/convert/mir.rs  |   1 +
 compiler/rustc_span/src/symbol.rs             |   1 +
 compiler/rustc_ty_utils/src/consts.rs         |   2 +-
 compiler/stable_mir/src/mir/body.rs           |   4 +
 library/core/src/cmp.rs                       |   8 +
 library/core/src/intrinsics.rs                |  12 ++
 library/core/tests/intrinsics.rs              |  19 +++
 library/core/tests/lib.rs                     |   1 +
 tests/assembly/x86_64-cmp.rs                  |  51 ++++++
 tests/codegen/intrinsics/three_way_compare.rs |  47 ++++++
 tests/mir-opt/lower_intrinsics.rs             |  15 ++
 ...pare_char.LowerIntrinsics.panic-abort.diff |  34 ++++
 ...are_char.LowerIntrinsics.panic-unwind.diff |  34 ++++
 ...re_signed.LowerIntrinsics.panic-abort.diff |  31 ++++
 ...e_signed.LowerIntrinsics.panic-unwind.diff |  31 ++++
 ..._unsigned.LowerIntrinsics.panic-abort.diff |  34 ++++
 ...unsigned.LowerIntrinsics.panic-unwind.diff |  34 ++++
 ....{impl#0}-partial_cmp.PreCodegen.after.mir | 151 +++++-------------
 35 files changed, 521 insertions(+), 119 deletions(-)
 create mode 100644 tests/assembly/x86_64-cmp.rs
 create mode 100644 tests/codegen/intrinsics/three_way_compare.rs
 create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-abort.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-unwind.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-abort.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-unwind.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-abort.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-unwind.diff

diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
index b2bc289a5b6b..4a5ef352151f 100644
--- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
+++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
@@ -68,7 +68,7 @@ pub(crate) fn maybe_codegen<'tcx>(
                 Some(CValue::by_val(ret_val, lhs.layout()))
             }
         }
-        BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None,
+        BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None,
         BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None,
     }
 }
@@ -134,6 +134,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
         BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),
         BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
         BinOp::Div | BinOp::Rem => unreachable!(),
+        BinOp::Cmp => unreachable!(),
         BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(),
         BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(),
     }
diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs
index 8992f40fb903..796182418ad6 100644
--- a/compiler/rustc_codegen_cranelift/src/num.rs
+++ b/compiler/rustc_codegen_cranelift/src/num.rs
@@ -40,6 +40,22 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option {
     })
 }
 
+fn codegen_three_way_compare<'tcx>(
+    fx: &mut FunctionCx<'_, '_, 'tcx>,
+    signed: bool,
+    lhs: Value,
+    rhs: Value,
+) -> CValue<'tcx> {
+    // This emits `(lhs > rhs) - (lhs < rhs)`, which is cranelift's preferred form per
+    // 
+    let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed).unwrap();
+    let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed).unwrap();
+    let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs);
+    let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs);
+    let val = fx.bcx.ins().isub(gt, lt);
+    CValue::by_val(val, fx.layout_of(fx.tcx.ty_ordering_enum(Some(fx.mir.span))))
+}
+
 fn codegen_compare_bin_op<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
     bin_op: BinOp,
@@ -47,6 +63,10 @@ fn codegen_compare_bin_op<'tcx>(
     lhs: Value,
     rhs: Value,
 ) -> CValue<'tcx> {
+    if bin_op == BinOp::Cmp {
+        return codegen_three_way_compare(fx, signed, lhs, rhs);
+    }
+
     let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap();
     let val = fx.bcx.ins().icmp(intcc, lhs, rhs);
     CValue::by_val(val, fx.layout_of(fx.tcx.types.bool))
@@ -59,7 +79,7 @@ pub(crate) fn codegen_binop<'tcx>(
     in_rhs: CValue<'tcx>,
 ) -> CValue<'tcx> {
     match bin_op {
-        BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
+        BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => {
             match in_lhs.layout().ty.kind() {
                 ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => {
                     let signed = type_sign(in_lhs.layout().ty);
@@ -160,7 +180,7 @@ pub(crate) fn codegen_int_binop<'tcx>(
         }
         BinOp::Offset => unreachable!("Offset is not an integer operation"),
         // Compare binops handles by `codegen_binop`.
-        BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
+        BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Cmp => {
             unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty);
         }
     };
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index d243d7088ada..78d943192db0 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -94,6 +94,10 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         self.const_int(self.type_i32(), i as i64)
     }
 
+    fn const_i8(&self, i: i8) -> RValue<'gcc> {
+        self.const_int(self.type_i8(), i as i64)
+    }
+
     fn const_u32(&self, i: u32) -> RValue<'gcc> {
         self.const_uint(self.type_u32(), i as u64)
     }
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 25cbd90460f2..568fcc3f3cf4 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -160,6 +160,10 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         self.const_int(self.type_i32(), i as i64)
     }
 
+    fn const_i8(&self, i: i8) -> &'ll Value {
+        self.const_int(self.type_i8(), i as i64)
+    }
+
     fn const_u32(&self, i: u32) -> &'ll Value {
         self.const_uint(self.type_i32(), i as u64)
     }
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 0e8c4abf2126..f0933fe33d74 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -7,6 +7,7 @@ use crate::common::{self, IntPredicate};
 use crate::traits::*;
 use crate::MemFlags;
 
+use rustc_hir as hir;
 use rustc_middle::mir;
 use rustc_middle::mir::Operand;
 use rustc_middle::ty::cast::{CastTy, IntTy};
@@ -882,6 +883,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs)
                 }
             }
+            mir::BinOp::Cmp => {
+                use std::cmp::Ordering;
+                debug_assert!(!is_float);
+                let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed);
+                if bx.cx().tcx().sess.opts.optimize == OptLevel::No {
+                    // FIXME: This actually generates tighter assembly, and is a classic trick
+                    // 
+                    // However, as of 2023-11 it optimizes worse in things like derived
+                    // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it
+                    // better (see ), it'll
+                    // be worth trying it in optimized builds as well.
+                    let is_gt = bx.icmp(pred(hir::BinOpKind::Gt), lhs, rhs);
+                    let gtext = bx.zext(is_gt, bx.type_i8());
+                    let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs);
+                    let ltext = bx.zext(is_lt, bx.type_i8());
+                    bx.unchecked_ssub(gtext, ltext)
+                } else {
+                    // These operations are those expected by `tests/codegen/integer-cmp.rs`,
+                    // from .
+                    let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs);
+                    let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs);
+                    let ge = bx.select(
+                        is_ne,
+                        bx.cx().const_i8(Ordering::Greater as i8),
+                        bx.cx().const_i8(Ordering::Equal as i8),
+                    );
+                    bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge)
+                }
+            }
         }
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs
index 4dff9c7684f1..8cb17a5b37a8 100644
--- a/compiler/rustc_codegen_ssa/src/traits/consts.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs
@@ -19,6 +19,7 @@ pub trait ConstMethods<'tcx>: BackendTypes {
     fn const_bool(&self, val: bool) -> Self::Value;
     fn const_i16(&self, i: i16) -> Self::Value;
     fn const_i32(&self, i: i32) -> Self::Value;
+    fn const_i8(&self, i: i8) -> Self::Value;
     fn const_u32(&self, i: u32) -> Self::Value;
     fn const_u64(&self, i: u64) -> Self::Value;
     fn const_u128(&self, i: u128) -> Self::Value;
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index dbc6a317640c..831787a92c86 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -235,6 +235,13 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
         Self::from_scalar(Scalar::from_bool(b), layout)
     }
 
+    #[inline]
+    pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self {
+        let ty = tcx.ty_ordering_enum(None);
+        let layout = tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap();
+        Self::from_scalar(Scalar::from_i8(c as i8), layout)
+    }
+
     #[inline]
     pub fn to_const_int(self) -> ConstInt {
         assert!(self.layout.ty.is_integral());
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 475c533077af..5665bb4999f5 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -61,6 +61,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 }
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
+    fn three_way_compare(&self, lhs: T, rhs: T) -> (ImmTy<'tcx, M::Provenance>, bool) {
+        let res = Ord::cmp(&lhs, &rhs);
+        return (ImmTy::from_ordering(res, *self.tcx), false);
+    }
+
     fn binary_char_op(
         &self,
         bin_op: mir::BinOp,
@@ -69,6 +74,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     ) -> (ImmTy<'tcx, M::Provenance>, bool) {
         use rustc_middle::mir::BinOp::*;
 
+        if bin_op == Cmp {
+            return self.three_way_compare(l, r);
+        }
+
         let res = match bin_op {
             Eq => l == r,
             Ne => l != r,
@@ -231,6 +240,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let r = self.sign_extend(r, right_layout) as i128;
                 return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false));
             }
+            if bin_op == Cmp {
+                let l = self.sign_extend(l, left_layout) as i128;
+                let r = self.sign_extend(r, right_layout) as i128;
+                return Ok(self.three_way_compare(l, r));
+            }
             let op: Option (i128, bool)> = match bin_op {
                 Div if r == 0 => throw_ub!(DivisionByZero),
                 Rem if r == 0 => throw_ub!(RemainderByZero),
@@ -270,6 +284,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             }
         }
 
+        if bin_op == Cmp {
+            return Ok(self.three_way_compare(l, r));
+        }
+
         let val = match bin_op {
             Eq => ImmTy::from_bool(l == r, *self.tcx),
             Ne => ImmTy::from_bool(l != r, *self.tcx),
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 1664475f52a1..6cde2296459e 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -987,6 +987,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             )
                         }
                     }
+                    Cmp => {
+                        for x in [a, b] {
+                            check_kinds!(
+                                x,
+                                "Cannot three-way compare non-integer type {:?}",
+                                ty::Char | ty::Uint(..) | ty::Int(..)
+                            )
+                        }
+                    }
                     AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr
                     | ShrUnchecked => {
                         for x in [a, b] {
diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs
index a8060463b69e..0c3b59a0e78e 100644
--- a/compiler/rustc_const_eval/src/util/mod.rs
+++ b/compiler/rustc_const_eval/src/util/mod.rs
@@ -19,7 +19,7 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool {
     match op {
         Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
         | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true,
-        Eq | Ne | Lt | Le | Gt | Ge => false,
+        Eq | Ne | Lt | Le | Gt | Ge | Cmp => false,
     }
 }
 
@@ -30,7 +30,7 @@ pub fn binop_right_homogeneous(op: mir::BinOp) -> bool {
     use rustc_middle::mir::BinOp::*;
     match op {
         Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
-        | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
+        | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge | Cmp => true,
         Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false,
     }
 }
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index dbf86f5cf747..ba971958aa92 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -225,6 +225,7 @@ language_item_table! {
     Unpin,                   sym::unpin,               unpin_trait,                Target::Trait,          GenericRequirement::None;
     Pin,                     sym::pin,                 pin_type,                   Target::Struct,         GenericRequirement::None;
 
+    OrderingEnum,            sym::Ordering,            ordering_enum,              Target::Enum,           GenericRequirement::Exact(0);
     PartialEq,               sym::eq,                  eq_trait,                   Target::Trait,          GenericRequirement::Exact(1);
     PartialOrd,              sym::partial_ord,         partial_ord_trait,          Target::Trait,          GenericRequirement::Exact(1);
     CVoid,                   sym::c_void,              c_void,                     Target::Enum,           GenericRequirement::None;
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index f482ae4f5fa0..93d71e4bfb93 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -107,6 +107,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
         | sym::cttz
         | sym::bswap
         | sym::bitreverse
+        | sym::three_way_compare
         | sym::discriminant_value
         | sym::type_id
         | sym::likely
@@ -418,6 +419,10 @@ pub fn check_intrinsic_type(
             | sym::bswap
             | sym::bitreverse => (1, 0, vec![param(0)], param(0)),
 
+            sym::three_way_compare => {
+                (1, 0, vec![param(0), param(0)], tcx.ty_ordering_enum(Some(span)))
+            }
+
             sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
                 (1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]))
             }
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 36b7a48b2a2b..a33de2efe2fa 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1444,6 +1444,8 @@ pub enum BinOp {
     Ge,
     /// The `>` operator (greater than)
     Gt,
+    /// The `<=>` operator (three-way comparison, like `Ord::cmp`)
+    Cmp,
     /// The `ptr.offset` operator
     Offset,
 }
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 56a0a6233970..74c2aef8fcc2 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -276,6 +276,11 @@ impl<'tcx> BinOp {
             &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
                 tcx.types.bool
             }
+            &BinOp::Cmp => {
+                // these should be integer-like types of the same size.
+                assert_eq!(lhs_ty, rhs_ty);
+                tcx.ty_ordering_enum(None)
+            }
         }
     }
 }
@@ -312,7 +317,8 @@ impl BinOp {
             BinOp::Gt => hir::BinOpKind::Gt,
             BinOp::Le => hir::BinOpKind::Le,
             BinOp::Ge => hir::BinOpKind::Ge,
-            BinOp::AddUnchecked
+            BinOp::Cmp
+            | BinOp::AddUnchecked
             | BinOp::SubUnchecked
             | BinOp::MulUnchecked
             | BinOp::ShlUnchecked
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 3393f4448438..01fd815be6d6 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -905,6 +905,13 @@ impl<'tcx> TyCtxt<'tcx> {
         self.get_lang_items(())
     }
 
+    /// Gets a `Ty` representing the [`LangItem::OrderingEnum`]
+    #[track_caller]
+    pub fn ty_ordering_enum(self, span: Option) -> Ty<'tcx> {
+        let ordering_enum = self.require_lang_item(hir::LangItem::OrderingEnum, span);
+        self.type_of(ordering_enum).no_bound_vars().unwrap()
+    }
+
     /// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to
     /// compare against another `DefId`, since `is_diagnostic_item` is cheaper.
     pub fn get_diagnostic_item(self, name: Symbol) -> Option {
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 7d4c1b9c21a6..7e8920604c17 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -90,6 +90,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
                     sym::wrapping_add
                     | sym::wrapping_sub
                     | sym::wrapping_mul
+                    | sym::three_way_compare
                     | sym::unchecked_add
                     | sym::unchecked_sub
                     | sym::unchecked_mul
@@ -109,6 +110,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
                             sym::wrapping_add => BinOp::Add,
                             sym::wrapping_sub => BinOp::Sub,
                             sym::wrapping_mul => BinOp::Mul,
+                            sym::three_way_compare => BinOp::Cmp,
                             sym::unchecked_add => BinOp::AddUnchecked,
                             sym::unchecked_sub => BinOp::SubUnchecked,
                             sym::unchecked_mul => BinOp::MulUnchecked,
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 2951897ebd69..d64cb9b12b2b 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -525,6 +525,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                     | BinOp::Lt
                     | BinOp::Ge
                     | BinOp::Gt
+                    | BinOp::Cmp
                     | BinOp::Offset
                     | BinOp::Add
                     | BinOp::AddUnchecked
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index b6a722da602e..22e7e5323c77 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -493,6 +493,7 @@ impl<'tcx> Stable<'tcx> for mir::BinOp {
             BinOp::Ne => stable_mir::mir::BinOp::Ne,
             BinOp::Ge => stable_mir::mir::BinOp::Ge,
             BinOp::Gt => stable_mir::mir::BinOp::Gt,
+            BinOp::Cmp => stable_mir::mir::BinOp::Cmp,
             BinOp::Offset => stable_mir::mir::BinOp::Offset,
         }
     }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 73fcd2a76dfc..7dffeab99228 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1785,6 +1785,7 @@ symbols! {
         thread,
         thread_local,
         thread_local_macro,
+        three_way_compare,
         thumb2,
         thumb_mode: "thumb-mode",
         tmm_reg,
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index 2a2e53a81ed8..acbcc3918b2f 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -82,7 +82,7 @@ fn check_binop(op: mir::BinOp) -> bool {
     match op {
         Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
         | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge
-        | Gt => true,
+        | Gt | Cmp => true,
         Offset => false,
     }
 }
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 7c536a3e9140..636bf919f5c6 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -329,6 +329,7 @@ pub enum BinOp {
     Ne,
     Ge,
     Gt,
+    Cmp,
     Offset,
 }
 
@@ -368,6 +369,9 @@ impl BinOp {
                 assert!(lhs_kind.is_primitive() || lhs_kind.is_raw_ptr() || lhs_kind.is_fn_ptr());
                 Ty::bool_ty()
             }
+            BinOp::Cmp => {
+                unimplemented!("Should cmp::Ordering be a RigidTy?");
+            }
         }
     }
 }
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index a2f07814726a..7581c6dd759c 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -376,6 +376,7 @@ pub struct AssertParamIsEq {
 /// ```
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), lang = "Ordering")]
 #[repr(i8)]
 pub enum Ordering {
     /// An ordering where a compared value is less than another.
@@ -1563,12 +1564,19 @@ mod impls {
             impl Ord for $t {
                 #[inline]
                 fn cmp(&self, other: &$t) -> Ordering {
+                    #[cfg(bootstrap)]
+                    {
                     // The order here is important to generate more optimal assembly.
                     // See  for more info.
                     if *self < *other { Less }
                     else if *self == *other { Equal }
                     else { Greater }
                 }
+                    #[cfg(not(bootstrap))]
+                    {
+                        crate::intrinsics::three_way_compare(*self, *other)
+                    }
+                }
             }
         )*)
     }
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index dec31548fc8c..af771b88dc30 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2146,6 +2146,18 @@ extern "rust-intrinsic" {
     #[rustc_nounwind]
     pub fn bitreverse(x: T) -> T;
 
+    /// Does a three-way comparison between the two integer arguments.
+    ///
+    /// This is included as an intrinsic as it's useful to let it be one thing
+    /// in MIR, rather than the multiple checks and switches that make its IR
+    /// large and difficult to optimize.
+    ///
+    /// The stabilized version of this intrinsic is [`Ord::cmp`].
+    #[cfg(not(bootstrap))]
+    #[rustc_const_unstable(feature = "const_three_way_compare", issue = "none")]
+    #[rustc_safe_intrinsic]
+    pub fn three_way_compare(lhs: T, rhs: T) -> crate::cmp::Ordering;
+
     /// Performs checked integer addition.
     ///
     /// Note that, unlike most intrinsics, this is safe to call;
diff --git a/library/core/tests/intrinsics.rs b/library/core/tests/intrinsics.rs
index 740565d0df6b..a9ef5415ada6 100644
--- a/library/core/tests/intrinsics.rs
+++ b/library/core/tests/intrinsics.rs
@@ -99,3 +99,22 @@ fn test_const_deallocate_at_runtime() {
         const_deallocate(core::ptr::null_mut(), 1, 1); // nop
     }
 }
+
+#[cfg(not(bootstrap))]
+#[test]
+fn test_three_way_compare_in_const_contexts() {
+    use core::cmp::Ordering::*;
+    use core::intrinsics::three_way_compare;
+
+    const {
+        assert!(Less as i8 == three_way_compare(123_u16, 456) as _);
+        assert!(Equal as i8 == three_way_compare(456_u16, 456) as _);
+        assert!(Greater as i8 == three_way_compare(789_u16, 456) as _);
+        assert!(Less as i8 == three_way_compare('A', 'B') as _);
+        assert!(Equal as i8 == three_way_compare('B', 'B') as _);
+        assert!(Greater as i8 == three_way_compare('C', 'B') as _);
+        assert!(Less as i8 == three_way_compare(-123_i16, 456) as _);
+        assert!(Equal as i8 == three_way_compare(456_i16, 456) as _);
+        assert!(Greater as i8 == three_way_compare(123_i16, -456) as _);
+    }
+}
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 421062f5873c..6a2ff3a2285a 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -21,6 +21,7 @@
 #![feature(const_pointer_is_aligned)]
 #![feature(const_ptr_as_ref)]
 #![feature(const_ptr_write)]
+#![cfg_attr(not(bootstrap), feature(const_three_way_compare))]
 #![feature(const_trait_impl)]
 #![feature(const_likely)]
 #![feature(const_location_fields)]
diff --git a/tests/assembly/x86_64-cmp.rs b/tests/assembly/x86_64-cmp.rs
new file mode 100644
index 000000000000..31efdda1bfaf
--- /dev/null
+++ b/tests/assembly/x86_64-cmp.rs
@@ -0,0 +1,51 @@
+//@ revisions: DEBUG OPTIM
+//@ [DEBUG] compile-flags: -C opt-level=0
+//@ [OPTIM] compile-flags: -C opt-level=3
+//@ assembly-output: emit-asm
+//@ compile-flags: --crate-type=lib -C llvm-args=-x86-asm-syntax=intel
+//@ only-x86_64
+//@ ignore-sgx
+
+#![feature(core_intrinsics)]
+
+use std::intrinsics::three_way_compare;
+
+#[no_mangle]
+// CHECK-LABEL: signed_cmp:
+pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering {
+    // DEBUG: cmp
+    // DEBUG: setg
+    // DEBUG: and
+    // DEBUG: cmp
+    // DEBUG: setl
+    // DEBUG: and
+    // DEBUG: sub
+
+    // OPTIM: xor
+    // OPTIM: cmp
+    // OPTIM: setne
+    // OPTIM: mov
+    // OPTIM: cmovge
+    // OPTIM: ret
+    three_way_compare(a, b)
+}
+
+#[no_mangle]
+// CHECK-LABEL: unsigned_cmp:
+pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering {
+    // DEBUG: cmp
+    // DEBUG: seta
+    // DEBUG: and
+    // DEBUG: cmp
+    // DEBUG: setb
+    // DEBUG: and
+    // DEBUG: sub
+
+    // OPTIM: xor
+    // OPTIM: cmp
+    // OPTIM: setne
+    // OPTIM: mov
+    // OPTIM: cmovae
+    // OPTIM: ret
+    three_way_compare(a, b)
+}
diff --git a/tests/codegen/intrinsics/three_way_compare.rs b/tests/codegen/intrinsics/three_way_compare.rs
new file mode 100644
index 000000000000..f3b631abc227
--- /dev/null
+++ b/tests/codegen/intrinsics/three_way_compare.rs
@@ -0,0 +1,47 @@
+//@ revisions: DEBUG OPTIM
+//@ [DEBUG] compile-flags: -C opt-level=0
+//@ [OPTIM] compile-flags: -C opt-level=3
+//@ compile-flags: -C no-prepopulate-passes
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+use std::intrinsics::three_way_compare;
+
+#[no_mangle]
+// CHECK-LABEL: @signed_cmp
+// DEBUG-SAME: (i16 %a, i16 %b)
+// OPTIM-SAME: (i16 noundef %a, i16 noundef %b)
+pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering {
+    // DEBUG: %[[GT:.+]] = icmp sgt i16 %a, %b
+    // DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
+    // DEBUG: %[[LT:.+]] = icmp slt i16 %a, %b
+    // DEBUG: %[[ZLT:.+]] = zext i1 %[[LT]] to i8
+    // DEBUG: %[[R:.+]] = sub nsw i8 %[[ZGT]], %[[ZLT]]
+
+    // OPTIM: %[[LT:.+]] = icmp slt i16 %a, %b
+    // OPTIM: %[[NE:.+]] = icmp ne i16 %a, %b
+    // OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
+    // OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
+    // OPTIM: ret i8 %[[CGEL]]
+    three_way_compare(a, b)
+}
+
+#[no_mangle]
+// CHECK-LABEL: @unsigned_cmp
+// DEBUG-SAME: (i16 %a, i16 %b)
+// OPTIM-SAME: (i16 noundef %a, i16 noundef %b)
+pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering {
+    // DEBUG: %[[GT:.+]] = icmp ugt i16 %a, %b
+    // DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
+    // DEBUG: %[[LT:.+]] = icmp ult i16 %a, %b
+    // DEBUG: %[[ZLT:.+]] = zext i1 %[[LT]] to i8
+    // DEBUG: %[[R:.+]] = sub nsw i8 %[[ZGT]], %[[ZLT]]
+
+    // OPTIM: %[[LT:.+]] = icmp ult i16 %a, %b
+    // OPTIM: %[[NE:.+]] = icmp ne i16 %a, %b
+    // OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
+    // OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
+    // OPTIM: ret i8 %[[CGEL]]
+    three_way_compare(a, b)
+}
diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs
index 278ddfce1c33..e9eb392e7952 100644
--- a/tests/mir-opt/lower_intrinsics.rs
+++ b/tests/mir-opt/lower_intrinsics.rs
@@ -229,3 +229,18 @@ pub unsafe fn ptr_offset(p: *const i32, d: isize) -> *const i32 {
 
     core::intrinsics::offset(p, d)
 }
+
+// EMIT_MIR lower_intrinsics.three_way_compare_char.LowerIntrinsics.diff
+pub fn three_way_compare_char(a: char, b: char) {
+    let _x = core::intrinsics::three_way_compare(a, b);
+}
+
+// EMIT_MIR lower_intrinsics.three_way_compare_signed.LowerIntrinsics.diff
+pub fn three_way_compare_signed(a: i16, b: i16) {
+    core::intrinsics::three_way_compare(a, b);
+}
+
+// EMIT_MIR lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.diff
+pub fn three_way_compare_unsigned(a: u32, b: u32) {
+    let _x = core::intrinsics::three_way_compare(a, b);
+}
diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-abort.diff
new file mode 100644
index 000000000000..816d62097158
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-abort.diff
@@ -0,0 +1,34 @@
+- // MIR for `three_way_compare_char` before LowerIntrinsics
++ // MIR for `three_way_compare_char` after LowerIntrinsics
+  
+  fn three_way_compare_char(_1: char, _2: char) -> () {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: ();
+      let _3: std::cmp::Ordering;
+      let mut _4: char;
+      let mut _5: char;
+      scope 1 {
+          debug _x => _3;
+      }
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _1;
+          StorageLive(_5);
+          _5 = _2;
+-         _3 = three_way_compare::(move _4, move _5) -> [return: bb1, unwind unreachable];
++         _3 = Cmp(move _4, move _5);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          StorageDead(_4);
+          _0 = const ();
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-unwind.diff
new file mode 100644
index 000000000000..80b4bd7a2be7
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-unwind.diff
@@ -0,0 +1,34 @@
+- // MIR for `three_way_compare_char` before LowerIntrinsics
++ // MIR for `three_way_compare_char` after LowerIntrinsics
+  
+  fn three_way_compare_char(_1: char, _2: char) -> () {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: ();
+      let _3: std::cmp::Ordering;
+      let mut _4: char;
+      let mut _5: char;
+      scope 1 {
+          debug _x => _3;
+      }
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _1;
+          StorageLive(_5);
+          _5 = _2;
+-         _3 = three_way_compare::(move _4, move _5) -> [return: bb1, unwind continue];
++         _3 = Cmp(move _4, move _5);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          StorageDead(_4);
+          _0 = const ();
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-abort.diff
new file mode 100644
index 000000000000..05c20aaa09a2
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-abort.diff
@@ -0,0 +1,31 @@
+- // MIR for `three_way_compare_signed` before LowerIntrinsics
++ // MIR for `three_way_compare_signed` after LowerIntrinsics
+  
+  fn three_way_compare_signed(_1: i16, _2: i16) -> () {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: ();
+      let _3: std::cmp::Ordering;
+      let mut _4: i16;
+      let mut _5: i16;
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _1;
+          StorageLive(_5);
+          _5 = _2;
+-         _3 = three_way_compare::(move _4, move _5) -> [return: bb1, unwind unreachable];
++         _3 = Cmp(move _4, move _5);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-unwind.diff
new file mode 100644
index 000000000000..8a254d02a47b
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-unwind.diff
@@ -0,0 +1,31 @@
+- // MIR for `three_way_compare_signed` before LowerIntrinsics
++ // MIR for `three_way_compare_signed` after LowerIntrinsics
+  
+  fn three_way_compare_signed(_1: i16, _2: i16) -> () {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: ();
+      let _3: std::cmp::Ordering;
+      let mut _4: i16;
+      let mut _5: i16;
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _1;
+          StorageLive(_5);
+          _5 = _2;
+-         _3 = three_way_compare::(move _4, move _5) -> [return: bb1, unwind continue];
++         _3 = Cmp(move _4, move _5);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-abort.diff
new file mode 100644
index 000000000000..437614ec6738
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-abort.diff
@@ -0,0 +1,34 @@
+- // MIR for `three_way_compare_unsigned` before LowerIntrinsics
++ // MIR for `three_way_compare_unsigned` after LowerIntrinsics
+  
+  fn three_way_compare_unsigned(_1: u32, _2: u32) -> () {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: ();
+      let _3: std::cmp::Ordering;
+      let mut _4: u32;
+      let mut _5: u32;
+      scope 1 {
+          debug _x => _3;
+      }
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _1;
+          StorageLive(_5);
+          _5 = _2;
+-         _3 = three_way_compare::(move _4, move _5) -> [return: bb1, unwind unreachable];
++         _3 = Cmp(move _4, move _5);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          StorageDead(_4);
+          _0 = const ();
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-unwind.diff
new file mode 100644
index 000000000000..7d6137979c81
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-unwind.diff
@@ -0,0 +1,34 @@
+- // MIR for `three_way_compare_unsigned` before LowerIntrinsics
++ // MIR for `three_way_compare_unsigned` after LowerIntrinsics
+  
+  fn three_way_compare_unsigned(_1: u32, _2: u32) -> () {
+      debug a => _1;
+      debug b => _2;
+      let mut _0: ();
+      let _3: std::cmp::Ordering;
+      let mut _4: u32;
+      let mut _5: u32;
+      scope 1 {
+          debug _x => _3;
+      }
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _1;
+          StorageLive(_5);
+          _5 = _2;
+-         _3 = three_way_compare::(move _4, move _5) -> [return: bb1, unwind continue];
++         _3 = Cmp(move _4, move _5);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          StorageDead(_4);
+          _0 = const ();
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
index dd7fce3ed0b4..7ce2d229d547 100644
--- a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
@@ -6,37 +6,33 @@ fn ::partial_cmp(_1: &MultiField, _2: &M
     let mut _0: std::option::Option;
     let mut _3: &char;
     let mut _4: &char;
-    let mut _10: std::option::Option;
+    let mut _8: std::option::Option;
+    let mut _9: i8;
+    let mut _10: &i16;
     let mut _11: &i16;
-    let mut _12: &i16;
-    let _18: std::option::Option;
     scope 1 {
-        debug cmp => _18;
+        debug cmp => _8;
     }
     scope 2 (inlined std::cmp::impls::::partial_cmp) {
         debug self => _3;
         debug other => _4;
-        let mut _9: std::cmp::Ordering;
+        let mut _7: std::cmp::Ordering;
         scope 3 (inlined std::cmp::impls::::cmp) {
             debug self => _3;
             debug other => _4;
             let mut _5: char;
             let mut _6: char;
-            let mut _7: bool;
-            let mut _8: bool;
         }
     }
     scope 4 (inlined std::cmp::impls::::partial_cmp) {
-        debug self => _11;
-        debug other => _12;
-        let mut _17: std::cmp::Ordering;
+        debug self => _10;
+        debug other => _11;
+        let mut _14: std::cmp::Ordering;
         scope 5 (inlined std::cmp::impls::::cmp) {
-            debug self => _11;
-            debug other => _12;
+            debug self => _10;
+            debug other => _11;
+            let mut _12: i16;
             let mut _13: i16;
-            let mut _14: i16;
-            let mut _15: bool;
-            let mut _16: bool;
         }
     }
 
@@ -45,115 +41,46 @@ fn ::partial_cmp(_1: &MultiField, _2: &M
         _3 = &((*_1).0: char);
         StorageLive(_4);
         _4 = &((*_2).0: char);
-        StorageLive(_9);
         StorageLive(_5);
-        StorageLive(_6);
-        StorageLive(_7);
         _5 = ((*_1).0: char);
+        StorageLive(_6);
         _6 = ((*_2).0: char);
-        _7 = Lt(_5, _6);
-        switchInt(move _7) -> [0: bb1, otherwise: bb10];
+        _7 = Cmp(move _5, move _6);
+        StorageDead(_6);
+        StorageDead(_5);
+        _8 = Option::::Some(_7);
+        StorageDead(_4);
+        StorageDead(_3);
+        _9 = discriminant(_7);
+        switchInt(move _9) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageLive(_8);
-        _8 = Eq(_5, _6);
-        switchInt(move _8) -> [0: bb2, otherwise: bb3];
+        StorageLive(_10);
+        _10 = &((*_1).1: i16);
+        StorageLive(_11);
+        _11 = &((*_2).1: i16);
+        StorageLive(_14);
+        StorageLive(_12);
+        _12 = ((*_1).1: i16);
+        StorageLive(_13);
+        _13 = ((*_2).1: i16);
+        _14 = Cmp(move _12, move _13);
+        StorageDead(_13);
+        StorageDead(_12);
+        _0 = Option::::Some(move _14);
+        StorageDead(_14);
+        StorageDead(_11);
+        StorageDead(_10);
+        goto -> bb3;
     }
 
     bb2: {
-        _9 = const Greater;
-        StorageDead(_8);
-        StorageDead(_7);
-        StorageDead(_6);
-        StorageDead(_5);
-        _10 = Option::::Some(move _9);
-        StorageDead(_9);
-        StorageDead(_4);
-        StorageDead(_3);
-        goto -> bb11;
+        _0 = _8;
+        goto -> bb3;
     }
 
     bb3: {
-        StorageDead(_8);
-        StorageDead(_7);
-        StorageDead(_6);
-        StorageDead(_5);
-        StorageDead(_9);
-        StorageDead(_4);
-        StorageDead(_3);
-        StorageLive(_11);
-        _11 = &((*_1).1: i16);
-        StorageLive(_12);
-        _12 = &((*_2).1: i16);
-        StorageLive(_17);
-        StorageLive(_13);
-        StorageLive(_14);
-        StorageLive(_15);
-        _13 = ((*_1).1: i16);
-        _14 = ((*_2).1: i16);
-        _15 = Lt(_13, _14);
-        switchInt(move _15) -> [0: bb4, otherwise: bb8];
-    }
-
-    bb4: {
-        StorageLive(_16);
-        _16 = Eq(_13, _14);
-        switchInt(move _16) -> [0: bb5, otherwise: bb6];
-    }
-
-    bb5: {
-        _17 = const Greater;
-        goto -> bb7;
-    }
-
-    bb6: {
-        _17 = const Equal;
-        goto -> bb7;
-    }
-
-    bb7: {
-        StorageDead(_16);
-        goto -> bb9;
-    }
-
-    bb8: {
-        _17 = const Less;
-        goto -> bb9;
-    }
-
-    bb9: {
-        StorageDead(_15);
-        StorageDead(_14);
-        StorageDead(_13);
-        _0 = Option::::Some(move _17);
-        StorageDead(_17);
-        StorageDead(_12);
-        StorageDead(_11);
-        goto -> bb12;
-    }
-
-    bb10: {
-        _9 = const Less;
-        StorageDead(_7);
-        StorageDead(_6);
-        StorageDead(_5);
-        _10 = Option::::Some(move _9);
-        StorageDead(_9);
-        StorageDead(_4);
-        StorageDead(_3);
-        goto -> bb11;
-    }
-
-    bb11: {
-        StorageLive(_18);
-        _18 = _10;
-        _0 = _10;
-        StorageDead(_18);
-        goto -> bb12;
-    }
-
-    bb12: {
         return;
     }
 }

From 142ef764ee3af3cee8d6336ee153a9e3f13fec90 Mon Sep 17 00:00:00 2001
From: 6d7a 
Date: Sun, 24 Mar 2024 10:19:25 +0100
Subject: [PATCH 090/192] fix: Check stack depth to prevent stack overflows in
 create_memory_map

---
 crates/hir-ty/src/consteval/tests.rs |  4 +-
 crates/hir-ty/src/mir/eval.rs        | 60 +++++++++++++++++++++-------
 2 files changed, 48 insertions(+), 16 deletions(-)

diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 9199663be12f..d1ffd5046c3f 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -2828,7 +2828,7 @@ fn unsized_local() {
 
 #[test]
 fn recursive_adt() {
-    check_answer(
+    check_fail(
         r#"
         //- minicore: coerce_unsized, index, slice
         pub enum TagTree {
@@ -2849,6 +2849,6 @@ fn recursive_adt() {
             TAG_TREE
         };
     "#,
-        |b, _| assert_eq!(b[0] % 8, 0),
+        |e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::StackOverflow)),
     );
 }
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 90b8092b4b12..035991b5e7cd 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1710,14 +1710,7 @@ impl Evaluator<'_> {
             }
             ConstScalar::Unknown => not_supported!("evaluating unknown const"),
         };
-        let patch_map = memory_map.transform_addresses(|b, mut align| {
-            // Prevent recursive addresses is adts and slices
-            match ((&b[..b.len() / 2]).try_into(), HEAP_OFFSET.checked_add(align)) {
-                (Ok(arr), Some(new_addr)) if usize::from_le_bytes(arr) == new_addr => {
-                    align *= 2;
-                }
-                _ => (),
-            };
+        let patch_map = memory_map.transform_addresses(|b, align| {
             let addr = self.heap_allocate(b.len(), align)?;
             self.write_memory(addr, b)?;
             Ok(addr.to_usize())
@@ -1938,7 +1931,11 @@ impl Evaluator<'_> {
             ty: &Ty,
             locals: &Locals,
             mm: &mut ComplexMemoryMap,
+            stack_depth_limit: usize,
         ) -> Result<()> {
+            if stack_depth_limit.checked_sub(1).is_none() {
+                return Err(MirEvalError::StackOverflow);
+            }
             match ty.kind(Interner) {
                 TyKind::Ref(_, _, t) => {
                     let size = this.size_align_of(t, locals)?;
@@ -1977,7 +1974,14 @@ impl Evaluator<'_> {
                             if let Some(ty) = check_inner {
                                 for i in 0..count {
                                     let offset = element_size * i;
-                                    rec(this, &b[offset..offset + element_size], ty, locals, mm)?;
+                                    rec(
+                                        this,
+                                        &b[offset..offset + element_size],
+                                        ty,
+                                        locals,
+                                        mm,
+                                        stack_depth_limit - 1,
+                                    )?;
                                 }
                             }
                         }
@@ -1991,7 +1995,14 @@ impl Evaluator<'_> {
                     let size = this.size_of_sized(inner, locals, "inner of array")?;
                     for i in 0..len {
                         let offset = i * size;
-                        rec(this, &bytes[offset..offset + size], inner, locals, mm)?;
+                        rec(
+                            this,
+                            &bytes[offset..offset + size],
+                            inner,
+                            locals,
+                            mm,
+                            stack_depth_limit - 1,
+                        )?;
                     }
                 }
                 chalk_ir::TyKind::Tuple(_, subst) => {
@@ -2000,7 +2011,14 @@ impl Evaluator<'_> {
                         let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
                         let offset = layout.fields.offset(id).bytes_usize();
                         let size = this.layout(ty)?.size.bytes_usize();
-                        rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+                        rec(
+                            this,
+                            &bytes[offset..offset + size],
+                            ty,
+                            locals,
+                            mm,
+                            stack_depth_limit - 1,
+                        )?;
                     }
                 }
                 chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
@@ -2015,7 +2033,14 @@ impl Evaluator<'_> {
                                 .bytes_usize();
                             let ty = &field_types[f].clone().substitute(Interner, subst);
                             let size = this.layout(ty)?.size.bytes_usize();
-                            rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+                            rec(
+                                this,
+                                &bytes[offset..offset + size],
+                                ty,
+                                locals,
+                                mm,
+                                stack_depth_limit - 1,
+                            )?;
                         }
                     }
                     AdtId::EnumId(e) => {
@@ -2034,7 +2059,14 @@ impl Evaluator<'_> {
                                     l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize();
                                 let ty = &field_types[f].clone().substitute(Interner, subst);
                                 let size = this.layout(ty)?.size.bytes_usize();
-                                rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+                                rec(
+                                    this,
+                                    &bytes[offset..offset + size],
+                                    ty,
+                                    locals,
+                                    mm,
+                                    stack_depth_limit - 1,
+                                )?;
                             }
                         }
                     }
@@ -2045,7 +2077,7 @@ impl Evaluator<'_> {
             Ok(())
         }
         let mut mm = ComplexMemoryMap::default();
-        rec(self, bytes, ty, locals, &mut mm)?;
+        rec(self, bytes, ty, locals, &mut mm, self.stack_depth_limit - 1)?;
         Ok(mm)
     }
 

From 2dfe7de8b66c17b3f9025766597b8399394527b8 Mon Sep 17 00:00:00 2001
From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com>
Date: Sun, 24 Mar 2024 15:39:15 +0100
Subject: [PATCH 091/192] Handle panicking like rustc CTFE does

Instead of using `core::fmt::format` to format panic messages, which may in turn
panic too and cause recursive panics and other messy things, redirect
`panic_fmt` to `const_panic_fmt` like CTFE, which in turn goes to
`panic_display` and does the things normally. See the tests for the full
call stack.
---
 crates/hir-ty/src/mir/eval.rs      |  5 ++-
 crates/hir-ty/src/mir/eval/shim.rs | 65 ++++++++++++------------------
 crates/ide/src/hover/tests.rs      | 26 ++++++++++++
 3 files changed, 56 insertions(+), 40 deletions(-)

diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index fd98141af63e..7799c039bda1 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -2317,7 +2317,7 @@ impl Evaluator<'_> {
 
     fn exec_fn_with_args(
         &mut self,
-        def: FunctionId,
+        mut def: FunctionId,
         args: &[IntervalAndTy],
         generic_args: Substitution,
         locals: &Locals,
@@ -2335,6 +2335,9 @@ impl Evaluator<'_> {
         )? {
             return Ok(None);
         }
+        if let Some(redirect_def) = self.detect_and_redirect_special_function(def)? {
+            def = redirect_def;
+        }
         let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
         match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? {
             MirOrDynIndex::Dyn(self_ty_idx) => {
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 628a1fe2d283..d4d669182f2e 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -13,7 +13,7 @@ use crate::mir::eval::{
     name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId,
     HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy,
     IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan,
-    ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
+    Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
 };
 
 mod simd;
@@ -158,6 +158,25 @@ impl Evaluator<'_> {
         Ok(false)
     }
 
+    pub(super) fn detect_and_redirect_special_function(
+        &mut self,
+        def: FunctionId,
+    ) -> Result> {
+        // `PanicFmt` is redirected to `ConstPanicFmt`
+        if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) {
+            let resolver =
+                self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast());
+
+            let Some(hir_def::lang_item::LangItemTarget::Function(const_panic_fmt)) =
+                self.db.lang_item(resolver.krate(), LangItem::ConstPanicFmt)
+            else {
+                not_supported!("const_panic_fmt lang item not found or not a function");
+            };
+            return Ok(Some(const_panic_fmt));
+        }
+        Ok(None)
+    }
+
     /// Clone has special impls for tuples and function pointers
     fn exec_clone(
         &mut self,
@@ -291,9 +310,14 @@ impl Evaluator<'_> {
         use LangItem::*;
         let candidate = self.db.lang_attr(def.into())?;
         // We want to execute these functions with special logic
-        if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
+        // `PanicFmt` is not detected here as it's redirected later.
+        if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
             return Some(candidate);
         }
+        if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() {
+            // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
+            return Some(LangItem::BeginPanic);
+        }
         None
     }
 
@@ -309,43 +333,6 @@ impl Evaluator<'_> {
         let mut args = args.iter();
         match it {
             BeginPanic => Err(MirEvalError::Panic("".to_owned())),
-            PanicFmt => {
-                let message = (|| {
-                    let resolver = self
-                        .db
-                        .crate_def_map(self.crate_id)
-                        .crate_root()
-                        .resolver(self.db.upcast());
-                    let Some(format_fn) = resolver.resolve_path_in_value_ns_fully(
-                        self.db.upcast(),
-                        &hir_def::path::Path::from_known_path_with_no_generic(
-                            ModPath::from_segments(
-                                hir_expand::mod_path::PathKind::Abs,
-                                [name![std], name![fmt], name![format]],
-                            ),
-                        ),
-                    ) else {
-                        not_supported!("std::fmt::format not found");
-                    };
-                    let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else {
-                        not_supported!("std::fmt::format is not a function")
-                    };
-                    let interval = self.interpret_mir(
-                        self.db
-                            .mir_body(format_fn.into())
-                            .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?,
-                        args.map(|x| IntervalOrOwned::Owned(x.clone())),
-                    )?;
-                    let message_string = interval.get(self)?;
-                    let addr =
-                        Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
-                    let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
-                    Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?)
-                        .into_owned())
-                })()
-                .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
-                Err(MirEvalError::Panic(message))
-            }
             SliceLen => {
                 let arg = args.next().ok_or(MirEvalError::InternalError(
                     "argument of <[T]>::len() is not provided".into(),
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 4451e31870f2..466e5570b337 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -5105,6 +5105,32 @@ fn foo(e: E) {
     );
 }
 
+#[test]
+fn hover_const_value() {
+    check(
+        r#"
+pub enum AA {
+    BB,
+}
+const CONST: AA = AA::BB;
+pub fn the_function() -> AA {
+    CON$0ST
+}
+"#,
+        expect![[r#"
+            *CONST*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const CONST: AA = BB
+            ```
+        "#]],
+    );
+}
+
 #[test]
 fn array_repeat_exp() {
     check(

From 8d5977d6af03af18c7851025b9ca70d0f2d12c86 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sun, 24 Mar 2024 10:01:37 -0700
Subject: [PATCH 092/192] Slightly simplify the `iN::partial_cmp` MIR

This saves some debug and scope metadata in every single function that calls it.

Normally wouldn't be worth it, but with the derives there's *so* many of these.
---
 library/core/src/cmp.rs                       | 21 ++++++++++++-------
 ....{impl#0}-partial_cmp.PreCodegen.after.mir | 18 +++++-----------
 2 files changed, 19 insertions(+), 20 deletions(-)

diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index 7581c6dd759c..e3b7cf84920e 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -1548,7 +1548,14 @@ mod impls {
             impl PartialOrd for $t {
                 #[inline]
                 fn partial_cmp(&self, other: &$t) -> Option {
-                    Some(self.cmp(other))
+                    #[cfg(bootstrap)]
+                    {
+                        Some(self.cmp(other))
+                    }
+                    #[cfg(not(bootstrap))]
+                    {
+                        Some(crate::intrinsics::three_way_compare(*self, *other))
+                    }
                 }
                 #[inline(always)]
                 fn lt(&self, other: &$t) -> bool { (*self) < (*other) }
@@ -1566,12 +1573,12 @@ mod impls {
                 fn cmp(&self, other: &$t) -> Ordering {
                     #[cfg(bootstrap)]
                     {
-                    // The order here is important to generate more optimal assembly.
-                    // See  for more info.
-                    if *self < *other { Less }
-                    else if *self == *other { Equal }
-                    else { Greater }
-                }
+                        // The order here is important to generate more optimal assembly.
+                        // See  for more info.
+                        if *self < *other { Less }
+                        else if *self == *other { Equal }
+                        else { Greater }
+                    }
                     #[cfg(not(bootstrap))]
                     {
                         crate::intrinsics::three_way_compare(*self, *other)
diff --git a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
index 7ce2d229d547..a6c644259124 100644
--- a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir
@@ -16,24 +16,16 @@ fn ::partial_cmp(_1: &MultiField, _2: &M
     scope 2 (inlined std::cmp::impls::::partial_cmp) {
         debug self => _3;
         debug other => _4;
+        let mut _5: char;
+        let mut _6: char;
         let mut _7: std::cmp::Ordering;
-        scope 3 (inlined std::cmp::impls::::cmp) {
-            debug self => _3;
-            debug other => _4;
-            let mut _5: char;
-            let mut _6: char;
-        }
     }
-    scope 4 (inlined std::cmp::impls::::partial_cmp) {
+    scope 3 (inlined std::cmp::impls::::partial_cmp) {
         debug self => _10;
         debug other => _11;
+        let mut _12: i16;
+        let mut _13: i16;
         let mut _14: std::cmp::Ordering;
-        scope 5 (inlined std::cmp::impls::::cmp) {
-            debug self => _10;
-            debug other => _11;
-            let mut _12: i16;
-            let mut _13: i16;
-        }
     }
 
     bb0: {

From c59e93c753ba7e5945fe95f4d1785b09300aa0e0 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sun, 24 Mar 2024 17:13:26 -0700
Subject: [PATCH 093/192] Address PR feedback

---
 compiler/rustc_middle/src/mir/syntax.rs |  8 +++++++
 library/core/src/cmp.rs                 |  3 +++
 library/core/tests/intrinsics.rs        | 32 +++++++++++++++----------
 3 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index a33de2efe2fa..67f881e862a1 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1445,6 +1445,14 @@ pub enum BinOp {
     /// The `>` operator (greater than)
     Gt,
     /// The `<=>` operator (three-way comparison, like `Ord::cmp`)
+    ///
+    /// This is supported only on the integer types and `char`, always returning
+    /// [`rustc_hir::LangItem::OrderingEnum`] (aka [`std::cmp::Ordering`]).
+    ///
+    /// [`Rvalue::BinaryOp`]`(BinOp::Cmp, A, B)` returns
+    /// - `Ordering::Less` (`-1_i8`, as a Scalar) if `A < B`
+    /// - `Ordering::Equal` (`0_i8`, as a Scalar) if `A == B`
+    /// - `Ordering::Greater` (`+1_i8`, as a Scalar) if `A > B`
     Cmp,
     /// The `ptr.offset` operator
     Offset,
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index e3b7cf84920e..7f9c041f6918 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -376,6 +376,9 @@ pub struct AssertParamIsEq {
 /// ```
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
 #[stable(feature = "rust1", since = "1.0.0")]
+// This is a lang item only so that `BinOp::Cmp` in MIR can return it.
+// It has no special behaviour, but does require that the three variants
+// `Less`/`Equal`/`Greater` remain `-1_i8`/`0_i8`/`+1_i8` respectively.
 #[cfg_attr(not(bootstrap), lang = "Ordering")]
 #[repr(i8)]
 pub enum Ordering {
diff --git a/library/core/tests/intrinsics.rs b/library/core/tests/intrinsics.rs
index a9ef5415ada6..eb1e1a0b9b1d 100644
--- a/library/core/tests/intrinsics.rs
+++ b/library/core/tests/intrinsics.rs
@@ -103,18 +103,26 @@ fn test_const_deallocate_at_runtime() {
 #[cfg(not(bootstrap))]
 #[test]
 fn test_three_way_compare_in_const_contexts() {
-    use core::cmp::Ordering::*;
+    use core::cmp::Ordering::{self, *};
     use core::intrinsics::three_way_compare;
 
-    const {
-        assert!(Less as i8 == three_way_compare(123_u16, 456) as _);
-        assert!(Equal as i8 == three_way_compare(456_u16, 456) as _);
-        assert!(Greater as i8 == three_way_compare(789_u16, 456) as _);
-        assert!(Less as i8 == three_way_compare('A', 'B') as _);
-        assert!(Equal as i8 == three_way_compare('B', 'B') as _);
-        assert!(Greater as i8 == three_way_compare('C', 'B') as _);
-        assert!(Less as i8 == three_way_compare(-123_i16, 456) as _);
-        assert!(Equal as i8 == three_way_compare(456_i16, 456) as _);
-        assert!(Greater as i8 == three_way_compare(123_i16, -456) as _);
-    }
+    const UNSIGNED_LESS: Ordering = three_way_compare(123_u16, 456);
+    const UNSIGNED_EQUAL: Ordering = three_way_compare(456_u16, 456);
+    const UNSIGNED_GREATER: Ordering = three_way_compare(789_u16, 456);
+    const CHAR_LESS: Ordering = three_way_compare('A', 'B');
+    const CHAR_EQUAL: Ordering = three_way_compare('B', 'B');
+    const CHAR_GREATER: Ordering = three_way_compare('C', 'B');
+    const SIGNED_LESS: Ordering = three_way_compare(123_i64, 456);
+    const SIGNED_EQUAL: Ordering = three_way_compare(456_i64, 456);
+    const SIGNED_GREATER: Ordering = three_way_compare(789_i64, 456);
+
+    assert_eq!(UNSIGNED_LESS, Less);
+    assert_eq!(UNSIGNED_EQUAL, Equal);
+    assert_eq!(UNSIGNED_GREATER, Greater);
+    assert_eq!(CHAR_LESS, Less);
+    assert_eq!(CHAR_EQUAL, Equal);
+    assert_eq!(CHAR_GREATER, Greater);
+    assert_eq!(SIGNED_LESS, Less);
+    assert_eq!(SIGNED_EQUAL, Equal);
+    assert_eq!(SIGNED_GREATER, Greater);
 }

From d81148a0095e97f00720d6bbeee098130b304696 Mon Sep 17 00:00:00 2001
From: Young-Flash 
Date: Thu, 21 Mar 2024 17:56:49 +0800
Subject: [PATCH 094/192] expose config for hover struct field display

---
 crates/rust-analyzer/src/config.rs | 3 +++
 docs/user/generated_config.adoc    | 5 +++++
 editors/code/package.json          | 9 +++++++++
 3 files changed, 17 insertions(+)

diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index e6b60f690659..a5545e798474 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -375,6 +375,8 @@ config_data! {
         /// How to render the size information in a memory layout hover.
         hover_memoryLayout_size: Option = "\"both\"",
 
+        /// How many fields of a struct to display when hovering a struct.
+        hover_show_structFields: Option = "null",
         /// How many associated items of a trait to display when hovering a trait.
         hover_show_traitAssocItems: Option = "null",
 
@@ -1690,6 +1692,7 @@ impl Config {
             },
             keywords: self.data.hover_documentation_keywords_enable,
             max_trait_assoc_items_count: self.data.hover_show_traitAssocItems,
+            max_struct_field_count: self.data.hover_show_structFields,
         }
     }
 
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 5e782b783111..c4024f6d282b 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -520,6 +520,11 @@ How to render the offset information in a memory layout hover.
 --
 How to render the size information in a memory layout hover.
 --
+[[rust-analyzer.hover.show.structFields]]rust-analyzer.hover.show.structFields (default: `null`)::
++
+--
+How many fields of a struct to display when hovering a struct.
+--
 [[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index c34b8e25de02..c3ea1ceeb692 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1144,6 +1144,15 @@
                         }
                     ]
                 },
+                "rust-analyzer.hover.show.structFields": {
+                    "markdownDescription": "How many fields of a struct to display when hovering a struct.",
+                    "default": null,
+                    "type": [
+                        "null",
+                        "integer"
+                    ],
+                    "minimum": 0
+                },
                 "rust-analyzer.hover.show.traitAssocItems": {
                     "markdownDescription": "How many associated items of a trait to display when hovering a trait.",
                     "default": null,

From 1c85234bcdbc7c863e737e9f3329518beeb436c3 Mon Sep 17 00:00:00 2001
From: Young-Flash 
Date: Thu, 21 Mar 2024 17:58:29 +0800
Subject: [PATCH 095/192] limit struct field hover display nums

---
 crates/hir/src/display.rs      | 35 ++++++++++++++++++++++------------
 crates/ide/src/hover.rs        |  1 +
 crates/ide/src/hover/render.rs |  3 +++
 crates/ide/src/static_index.rs |  1 +
 4 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index c5d44c11f2c1..0fa2d803467a 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -185,19 +185,30 @@ impl HirDisplay for Struct {
                 write_where_clause(def_id, f)?;
             }
             StructKind::Record => {
-                let has_where_clause = write_where_clause(def_id, f)?;
-                let fields = self.fields(f.db);
-                f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
-                if fields.is_empty() {
-                    f.write_str("{}")?;
-                } else {
-                    f.write_str("{\n")?;
-                    for field in self.fields(f.db) {
-                        f.write_str("    ")?;
-                        field.hir_fmt(f)?;
-                        f.write_str(",\n")?;
+                if let Some(limit) = f.entity_limit {
+                    let has_where_clause = write_where_clause(def_id, f)?;
+                    let fields = self.fields(f.db);
+                    let count = fields.len().min(limit);
+                    f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
+                    if count == 0 {
+                        if fields.is_empty() {
+                            f.write_str("{}")?;
+                        } else {
+                            f.write_str("{ /* … */ }")?;
+                        }
+                    } else {
+                        f.write_str(" {\n")?;
+                        for field in &fields[..count] {
+                            f.write_str("    ")?;
+                            field.hir_fmt(f)?;
+                            f.write_str(",\n")?;
+                        }
+
+                        if fields.len() > count {
+                            f.write_str("    /* … */\n")?;
+                        }
+                        f.write_str("}")?;
                     }
-                    f.write_str("}")?;
                 }
             }
             StructKind::Unit => _ = write_where_clause(def_id, f)?,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 8f4c629b5813..822751c0e4ce 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -33,6 +33,7 @@ pub struct HoverConfig {
     pub keywords: bool,
     pub format: HoverDocFormat,
     pub max_trait_assoc_items_count: Option,
+    pub max_struct_field_count: Option,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 63777d491050..abedbff831a5 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -410,6 +410,9 @@ pub(super) fn definition(
         Definition::Trait(trait_) => {
             trait_.display_limited(db, config.max_trait_assoc_items_count).to_string()
         }
+        Definition::Adt(Adt::Struct(struct_)) => {
+            struct_.display_limited(db, config.max_struct_field_count).to_string()
+        }
         _ => def.label(db),
     };
     let docs = def.docs(db, famous_defs);
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index fe063081f799..3fef16df25e3 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -167,6 +167,7 @@ impl StaticIndex<'_> {
             keywords: true,
             format: crate::HoverDocFormat::Markdown,
             max_trait_assoc_items_count: None,
+            max_struct_field_count: None,
         };
         let tokens = tokens.filter(|token| {
             matches!(

From a89e417ce5b23b304ec17d05ca2786ba364f1d67 Mon Sep 17 00:00:00 2001
From: Young-Flash 
Date: Thu, 21 Mar 2024 17:58:44 +0800
Subject: [PATCH 096/192] adjust test

---
 crates/ide/src/hover/tests.rs | 40 +++++++++++++----------------------
 1 file changed, 15 insertions(+), 25 deletions(-)

diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 466e5570b337..289c2ad945da 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -18,6 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
     format: HoverDocFormat::Markdown,
     keywords: true,
     max_trait_assoc_items_count: None,
+    max_struct_field_count: None,
 };
 
 fn check_hover_no_result(ra_fixture: &str) {
@@ -853,9 +854,7 @@ struct Foo$0 { field: u32 }
 
             ```rust
             // size = 4, align = 4
-            struct Foo {
-                field: u32,
-            }
+            struct Foo
             ```
         "#]],
     );
@@ -873,11 +872,6 @@ struct Foo$0 where u32: Copy { field: u32 }
             ```rust
             // size = 4, align = 4
             struct Foo
-            where
-                u32: Copy,
-            {
-                field: u32,
-            }
             ```
         "#]],
     );
@@ -1344,9 +1338,7 @@ impl Thing {
                 ```
 
                 ```rust
-                struct Thing {
-                    x: u32,
-                }
+                struct Thing
                 ```
             "#]],
     );
@@ -1365,9 +1357,7 @@ impl Thing {
                 ```
 
                 ```rust
-                struct Thing {
-                    x: u32,
-                }
+                struct Thing
                 ```
             "#]],
     );
@@ -2599,7 +2589,7 @@ fn main() { let s$0t = S{ f1:0 }; }
                                     focus_range: 7..8,
                                     name: "S",
                                     kind: Struct,
-                                    description: "struct S {\n    f1: u32,\n}",
+                                    description: "struct S",
                                 },
                             },
                         ],
@@ -2645,7 +2635,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
                                 focus_range: 24..25,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {\n    f1: T,\n}",
+                                description: "struct S",
                             },
                         },
                     ],
@@ -2704,7 +2694,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
                                 focus_range: 24..25,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {\n    f1: T,\n}",
+                                description: "struct S",
                             },
                         },
                     ],
@@ -2957,7 +2947,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 39..41,
                                 name: "S1",
                                 kind: Struct,
-                                description: "struct S1 {}",
+                                description: "struct S1",
                             },
                         },
                         HoverGotoTypeData {
@@ -2970,7 +2960,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 52..54,
                                 name: "S2",
                                 kind: Struct,
-                                description: "struct S2 {}",
+                                description: "struct S2",
                             },
                         },
                     ],
@@ -3061,7 +3051,7 @@ fn foo(ar$0g: &impl Foo + Bar) {}
                                 focus_range: 36..37,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {}",
+                                description: "struct S",
                             },
                         },
                     ],
@@ -3161,7 +3151,7 @@ fn foo(ar$0g: &impl Foo) {}
                                 focus_range: 23..24,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {}",
+                                description: "struct S",
                             },
                         },
                     ],
@@ -3198,7 +3188,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 49..50,
                                 name: "B",
                                 kind: Struct,
-                                description: "struct B {}",
+                                description: "struct B",
                             },
                         },
                         HoverGotoTypeData {
@@ -3287,7 +3277,7 @@ fn foo(ar$0g: &dyn Foo) {}
                                 focus_range: 23..24,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {}",
+                                description: "struct S",
                             },
                         },
                     ],
@@ -3322,7 +3312,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {}
                                 focus_range: 50..51,
                                 name: "B",
                                 kind: Struct,
-                                description: "struct B {}",
+                                description: "struct B",
                             },
                         },
                         HoverGotoTypeData {
@@ -3361,7 +3351,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {}
                                 focus_range: 65..66,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {}",
+                                description: "struct S",
                             },
                         },
                     ],

From ba8c9810aadc2e2773ba1c89b621d100a39e35df Mon Sep 17 00:00:00 2001
From: Young-Flash 
Date: Sat, 23 Mar 2024 08:42:45 +0800
Subject: [PATCH 097/192] review update

---
 crates/hir/src/display.rs     | 2 +-
 crates/ide/src/hover/tests.rs | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 0fa2d803467a..23c6b078b969 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -185,8 +185,8 @@ impl HirDisplay for Struct {
                 write_where_clause(def_id, f)?;
             }
             StructKind::Record => {
+                let has_where_clause = write_where_clause(def_id, f)?;
                 if let Some(limit) = f.entity_limit {
-                    let has_where_clause = write_where_clause(def_id, f)?;
                     let fields = self.fields(f.db);
                     let count = fields.len().min(limit);
                     f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 289c2ad945da..463f0951eafc 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -872,6 +872,8 @@ struct Foo$0 where u32: Copy { field: u32 }
             ```rust
             // size = 4, align = 4
             struct Foo
+            where
+                u32: Copy,
             ```
         "#]],
     );

From 58013ad70d2c8a204d311fdacb40a80ba87f37c4 Mon Sep 17 00:00:00 2001
From: Young-Flash 
Date: Mon, 25 Mar 2024 19:55:09 +0800
Subject: [PATCH 098/192] add test for struct field hover display limit

---
 crates/ide/src/hover/tests.rs | 91 +++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 463f0951eafc..192f6c0272ba 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -50,6 +50,28 @@ fn check(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
+#[track_caller]
+fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) {
+    let (analysis, position) = fixture::position(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig {
+                links_in_hover: true,
+                max_struct_field_count: Some(count),
+                ..HOVER_BASE_CONFIG
+            },
+            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+        )
+        .unwrap()
+        .unwrap();
+
+    let content = analysis.db.file_text(position.file_id);
+    let hovered_element = &content[hover.range];
+
+    let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup);
+    expect.assert_eq(&actual)
+}
+
 #[track_caller]
 fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
@@ -879,6 +901,75 @@ struct Foo$0 where u32: Copy { field: u32 }
     );
 }
 
+#[test]
+fn hover_record_struct_limit() {
+    check_hover_struct_limit(
+        3,
+        r#"
+    struct Foo$0 { a: u32, b: i32, c: i32 }
+    "#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 12 (0xC), align = 4
+            struct Foo  {
+                a: u32,
+                b: i32,
+                c: i32,
+            }
+            ```
+        "#]],
+    );
+    check_hover_struct_limit(
+        3,
+        r#"
+    struct Foo$0 { a: u32 }
+    "#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 4, align = 4
+            struct Foo  {
+                a: u32,
+            }
+            ```
+        "#]],
+    );
+    check_hover_struct_limit(
+        3,
+        r#"
+    struct Foo$0 { a: u32, b: i32, c: i32, d: u32 }
+    "#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 16 (0x10), align = 4
+            struct Foo  {
+                a: u32,
+                b: i32,
+                c: i32,
+                /* … */
+            }
+            ```
+        "#]],
+    );
+}
+
 #[test]
 fn hover_unit_struct() {
     check(

From 1716cc8433fe4822cf531905b8d132c23592c72c Mon Sep 17 00:00:00 2001
From: Nadrieril 
Date: Mon, 25 Mar 2024 13:09:37 +0100
Subject: [PATCH 099/192] Revert to the crates.io version of
 rustc_pattern_analysis

---
 crates/hir-ty/src/lib.rs | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index d6d5c16ae75b..579ca8a82504 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -15,10 +15,8 @@ extern crate rustc_abi;
 #[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_abi as rustc_abi;
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_pattern_analysis;
-
-#[cfg(not(feature = "in-rust-tree"))]
+// Use the crates.io version unconditionally until the API settles enough that we can switch to
+// using the in-tree one.
 extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
 
 mod builder;

From 0e54e2b55ac3d2ddec269978c86787e7320aaa7a Mon Sep 17 00:00:00 2001
From: dfireBird 
Date: Tue, 19 Mar 2024 12:57:18 +0530
Subject: [PATCH 100/192] use references in Generics iter methods

---
 crates/hir-def/src/generics.rs |  6 +++++
 crates/hir-ty/src/display.rs   |  2 +-
 crates/hir-ty/src/lower.rs     | 10 ++++----
 crates/hir-ty/src/utils.rs     | 43 +++++++++++++++++-----------------
 4 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index c141acd2ec48..4bc86623dfb8 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -142,6 +142,12 @@ impl GenericParamData {
 
 impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
 
+pub enum GenericParamDataRef<'a> {
+    TypeParamData(&'a TypeParamData),
+    ConstParamData(&'a ConstParamData),
+    LifetimeParamData(&'a LifetimeParamData),
+}
+
 /// Data about the generic parameters of a function, struct, impl, etc.
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct GenericParams {
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index abac34470359..8740ae6797cb 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -3,7 +3,6 @@
 //! purposes.
 
 use std::{
-    cmp::Ordering,
     fmt::{self, Debug},
     mem::size_of,
 };
@@ -1324,6 +1323,7 @@ fn hir_fmt_generics(
 ) -> Result<(), HirDisplayError> {
     let db = f.db;
     if parameters.len(Interner) > 0 {
+        use std::cmp::Ordering;
         let param_compare =
             |a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) {
                 (crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => {
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index d0d30fd570c1..25ccc84c13c8 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -24,7 +24,7 @@ use hir_def::{
     data::adt::StructKind,
     expander::Expander,
     generics::{
-        GenericParamData, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
+        GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
         WherePredicateTypeTarget,
     },
     lang_item::LangItem,
@@ -356,7 +356,7 @@ impl<'a> TyLoweringContext<'a> {
                                 .filter(|(_, data)| {
                                     matches!(
                                         data,
-                                        GenericParamData::TypeParamData(data)
+                                        GenericParamDataRef::TypeParamData(data)
                                         if data.provenance == TypeParamProvenance::ArgumentImplTrait
                                     )
                                 })
@@ -1770,7 +1770,7 @@ pub(crate) fn generic_defaults_query(
 
     let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| {
         match p {
-            GenericParamData::TypeParamData(p) => {
+            GenericParamDataRef::TypeParamData(p) => {
                 let mut ty =
                     p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
                 // Each default can only refer to previous parameters.
@@ -1779,7 +1779,7 @@ pub(crate) fn generic_defaults_query(
                 ty = fallback_bound_vars(ty, idx, parent_start_idx);
                 crate::make_binders(db, &generic_params, ty.cast(Interner))
             }
-            GenericParamData::ConstParamData(p) => {
+            GenericParamDataRef::ConstParamData(p) => {
                 let GenericParamId::ConstParamId(id) = id else {
                     unreachable!("Unexpected lifetime or type argument")
                 };
@@ -1795,7 +1795,7 @@ pub(crate) fn generic_defaults_query(
                 val = fallback_bound_vars(val, idx, parent_start_idx);
                 make_binders(db, &generic_params, val)
             }
-            GenericParamData::LifetimeParamData(_) => {
+            GenericParamDataRef::LifetimeParamData(_) => {
                 // using static because it requires defaults
                 make_binders(db, &generic_params, static_lifetime().cast(Interner))
             }
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index af5f7bb42a52..9b8a3e2dfbf7 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -12,7 +12,7 @@ use chalk_ir::{
 use hir_def::{
     db::DefDatabase,
     generics::{
-        GenericParamData, GenericParams, LifetimeParamData, TypeOrConstParamData,
+        GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData,
         TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
     },
     lang_item::LangItem,
@@ -277,28 +277,28 @@ impl Generics {
     /// Iterator over types and const params of self, then parent.
     pub(crate) fn iter<'a>(
         &'a self,
-    ) -> impl DoubleEndedIterator + 'a {
+    ) -> impl DoubleEndedIterator)> + 'a {
         let from_toc_id = |it: &'a Generics| {
-            move |(local_id, p): (_, &TypeOrConstParamData)| {
+            move |(local_id, p): (_, &'a TypeOrConstParamData)| {
                 let id = TypeOrConstParamId { parent: it.def, local_id };
                 match p {
                     TypeOrConstParamData::TypeParamData(p) => (
                         GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
-                        GenericParamData::TypeParamData(p.clone()),
+                        GenericParamDataRef::TypeParamData(p),
                     ),
                     TypeOrConstParamData::ConstParamData(p) => (
                         GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
-                        GenericParamData::ConstParamData(p.clone()),
+                        GenericParamDataRef::ConstParamData(p),
                     ),
                 }
             }
         };
 
         let from_lt_id = |it: &'a Generics| {
-            move |(local_id, p): (_, &LifetimeParamData)| {
+            move |(local_id, p): (_, &'a LifetimeParamData)| {
                 (
                     GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
-                    GenericParamData::LifetimeParamData(p.clone()),
+                    GenericParamDataRef::LifetimeParamData(p),
                 )
             }
         };
@@ -310,28 +310,28 @@ impl Generics {
     /// Iterate over types and const params without parent params.
     pub(crate) fn iter_self<'a>(
         &'a self,
-    ) -> impl DoubleEndedIterator + 'a {
+    ) -> impl DoubleEndedIterator)> + 'a {
         let from_toc_id = |it: &'a Generics| {
-            move |(local_id, p): (_, &TypeOrConstParamData)| {
+            move |(local_id, p): (_, &'a TypeOrConstParamData)| {
                 let id = TypeOrConstParamId { parent: it.def, local_id };
                 match p {
                     TypeOrConstParamData::TypeParamData(p) => (
                         GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
-                        GenericParamData::TypeParamData(p.clone()),
+                        GenericParamDataRef::TypeParamData(p),
                     ),
                     TypeOrConstParamData::ConstParamData(p) => (
                         GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
-                        GenericParamData::ConstParamData(p.clone()),
+                        GenericParamDataRef::ConstParamData(p),
                     ),
                 }
             }
         };
 
         let from_lt_id = |it: &'a Generics| {
-            move |(local_id, p): (_, &LifetimeParamData)| {
+            move |(local_id, p): (_, &'a LifetimeParamData)| {
                 (
                     GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
-                    GenericParamData::LifetimeParamData(p.clone()),
+                    GenericParamDataRef::LifetimeParamData(p),
                 )
             }
         };
@@ -340,28 +340,29 @@ impl Generics {
     }
 
     /// Iterator over types and const params of parent.
-    pub(crate) fn iter_parent(
-        &self,
-    ) -> impl DoubleEndedIterator + '_ {
+    #[allow(clippy::needless_lifetimes)]
+    pub(crate) fn iter_parent<'a>(
+        &'a self,
+    ) -> impl DoubleEndedIterator)> + 'a {
         self.parent_generics().into_iter().flat_map(|it| {
-            let from_toc_id = move |(local_id, p): (_, &TypeOrConstParamData)| {
+            let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| {
                 let id = TypeOrConstParamId { parent: it.def, local_id };
                 match p {
                     TypeOrConstParamData::TypeParamData(p) => (
                         GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
-                        GenericParamData::TypeParamData(p.clone()),
+                        GenericParamDataRef::TypeParamData(p),
                     ),
                     TypeOrConstParamData::ConstParamData(p) => (
                         GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
-                        GenericParamData::ConstParamData(p.clone()),
+                        GenericParamDataRef::ConstParamData(p),
                     ),
                 }
             };
 
-            let from_lt_id = move |(local_id, p): (_, &LifetimeParamData)| {
+            let from_lt_id = move |(local_id, p): (_, &'a LifetimeParamData)| {
                 (
                     GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
-                    GenericParamData::LifetimeParamData(p.clone()),
+                    GenericParamDataRef::LifetimeParamData(p),
                 )
             };
             let lt_iter = it.params.iter_lt().map(from_lt_id);

From 85947bba495e5029500046b01a745658ea8fe108 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Tue, 26 Mar 2024 10:58:35 +0200
Subject: [PATCH 101/192] Update comment on provenance_split

---
 crates/hir-ty/src/utils.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 9b8a3e2dfbf7..6a96ca804c9f 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -387,7 +387,7 @@ impl Generics {
         self.params.type_or_consts.len()
     }
 
-    /// (parent total, self param, type param list, const param list, impl trait)
+    /// (parent total, self param, type params, const params, impl trait list, lifetimes)
     pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize, usize) {
         let mut self_params = 0;
         let mut type_params = 0;

From 8f9a58c73d4181f137f92a83377cd4ca0a9b0259 Mon Sep 17 00:00:00 2001
From: "Alexis (Poliorcetics) Bourget" 
Date: Wed, 27 Mar 2024 02:29:03 +0100
Subject: [PATCH 102/192] fix: check for client support of relative glob
 patterns before using them

---
 crates/rust-analyzer/src/config.rs | 11 +++++++++
 crates/rust-analyzer/src/reload.rs | 36 +++++++++++++++++++++---------
 2 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index a5545e798474..7475a8e6e6d9 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -1013,6 +1013,17 @@ impl Config {
         )
     }
 
+    pub fn did_change_watched_files_relative_pattern_support(&self) -> bool {
+        try_or_def!(
+            self.caps
+                .workspace
+                .as_ref()?
+                .did_change_watched_files
+                .as_ref()?
+                .relative_pattern_support?
+        )
+    }
+
     pub fn prefill_caches(&self) -> bool {
         self.data.cachePriming_enable
     }
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 499e779978b9..771a5599f6f5 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -428,16 +428,16 @@ impl GlobalState {
         }
 
         if let FilesWatcher::Client = self.config.files().watcher {
-            let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
-                watchers: self
-                    .workspaces
-                    .iter()
-                    .flat_map(|ws| ws.to_roots())
-                    .filter(|it| it.is_local)
+            let filter =
+                self.workspaces.iter().flat_map(|ws| ws.to_roots()).filter(|it| it.is_local);
+
+            let watchers = if self.config.did_change_watched_files_relative_pattern_support() {
+                // When relative patterns are supported by the client, prefer using them
+                filter
                     .flat_map(|root| {
-                        root.include
-                            .into_iter()
-                            .flat_map(|it| [(it.clone(), "**/*.rs"), (it, "**/Cargo.{lock,toml}")])
+                        root.include.into_iter().flat_map(|base| {
+                            [(base.clone(), "**/*.rs"), (base, "**/Cargo.{lock,toml}")]
+                        })
                     })
                     .map(|(base, pat)| lsp_types::FileSystemWatcher {
                         glob_pattern: lsp_types::GlobPattern::Relative(
@@ -450,8 +450,24 @@ impl GlobalState {
                         ),
                         kind: None,
                     })
-                    .collect(),
+                    .collect()
+            } else {
+                // When they're not, integrate the base to make them into absolute patterns
+                filter
+                    .flat_map(|root| {
+                        root.include.into_iter().flat_map(|base| {
+                            [format!("{base}/**/*.rs"), format!("{base}/**/Cargo.{{lock,toml}}")]
+                        })
+                    })
+                    .map(|glob_pattern| lsp_types::FileSystemWatcher {
+                        glob_pattern: lsp_types::GlobPattern::String(glob_pattern),
+                        kind: None,
+                    })
+                    .collect()
             };
+
+            let registration_options =
+                lsp_types::DidChangeWatchedFilesRegistrationOptions { watchers };
             let registration = lsp_types::Registration {
                 id: "workspace/didChangeWatchedFiles".to_owned(),
                 method: "workspace/didChangeWatchedFiles".to_owned(),

From 7e4bc4a373574ca727254cc7051ee42cb1102e06 Mon Sep 17 00:00:00 2001
From: Michael Woerister 
Date: Wed, 27 Mar 2024 14:57:01 +0100
Subject: [PATCH 103/192] Remove and disallow HashStable impl of HashMap.

---
 .../src/stable_hasher.rs                      | 48 +------------------
 1 file changed, 2 insertions(+), 46 deletions(-)

diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index 15691804a94b..8418b4bbd470 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -684,26 +684,11 @@ where
 impl_stable_traits_for_trivial_type!(::std::path::Path);
 impl_stable_traits_for_trivial_type!(::std::path::PathBuf);
 
-impl HashStable for ::std::collections::HashMap
-where
-    K: ToStableHashKey + Eq,
-    V: HashStable,
-    R: BuildHasher,
-{
-    #[inline]
-    fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
-        stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, (key, value)| {
-            let key = key.to_stable_hash_key(hcx);
-            key.hash_stable(hcx, hasher);
-            value.hash_stable(hcx, hasher);
-        });
-    }
-}
-
-// It is not safe to implement HashStable for HashSet or any other collection type
+// It is not safe to implement HashStable for HashSet, HashMap or any other collection type
 // with unstable but observable iteration order.
 // See https://github.com/rust-lang/compiler-team/issues/533 for further information.
 impl !HashStable for std::collections::HashSet {}
+impl !HashStable for std::collections::HashMap {}
 
 impl HashStable for ::std::collections::BTreeMap
 where
@@ -730,35 +715,6 @@ where
     }
 }
 
-fn stable_hash_reduce(
-    hcx: &mut HCX,
-    hasher: &mut StableHasher,
-    mut collection: C,
-    length: usize,
-    hash_function: F,
-) where
-    C: Iterator,
-    F: Fn(&mut StableHasher, &mut HCX, I),
-{
-    length.hash_stable(hcx, hasher);
-
-    match length {
-        1 => {
-            hash_function(hasher, hcx, collection.next().unwrap());
-        }
-        _ => {
-            let hash = collection
-                .map(|value| {
-                    let mut hasher = StableHasher::new();
-                    hash_function(&mut hasher, hcx, value);
-                    hasher.finish::()
-                })
-                .reduce(|accum, value| accum.wrapping_add(value));
-            hash.hash_stable(hcx, hasher);
-        }
-    }
-}
-
 /// Controls what data we do or do not hash.
 /// Whenever a `HashStable` implementation caches its
 /// result, it needs to include `HashingControls` as part

From 23c9f698c09d44d6d8ea27fa7631202f26209be3 Mon Sep 17 00:00:00 2001
From: Nadrieril 
Date: Mon, 4 Mar 2024 01:57:39 +0100
Subject: [PATCH 104/192] Avoid recursion in creating and merging or-patterns

By calling back into `match_candidates`, we only need to expand one
layer at a time. Conversely, since we always try to simplify a layer
that we just expanded, we only have to merge one layer at a time.
---
 .../rustc_mir_build/src/build/matches/mod.rs   |  6 +-----
 .../src/build/matches/simplify.rs              | 18 ++----------------
 2 files changed, 3 insertions(+), 21 deletions(-)

diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 6d083e66a9b2..0fb5ed89437e 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1299,7 +1299,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 for candidate in candidates.iter_mut() {
                     candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate));
                 }
-                self.match_simplified_candidates(
+                self.match_candidates(
                     span,
                     scrutinee_span,
                     start_block,
@@ -1556,11 +1556,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
 
         let mut can_merge = true;
-
-        // Not `Iterator::all` because we don't want to short-circuit.
         for subcandidate in &mut candidate.subcandidates {
-            self.merge_trivial_subcandidates(subcandidate);
-
             // FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
             can_merge &=
                 subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty();
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs
index bf1906f370b6..0c4d45ea0504 100644
--- a/compiler/rustc_mir_build/src/build/matches/simplify.rs
+++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs
@@ -67,26 +67,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         debug!(simplified = ?match_pairs, "simplify_match_pairs");
     }
 
-    /// Create a new candidate for each pattern in `pats`, and recursively simplify tje
-    /// single-or-pattern case.
+    /// Create a new candidate for each pattern in `pats`.
     pub(super) fn create_or_subcandidates<'pat>(
         &mut self,
         pats: &[FlatPat<'pat, 'tcx>],
         has_guard: bool,
     ) -> Vec> {
-        pats.iter()
-            .cloned()
-            .map(|flat_pat| {
-                let mut candidate = Candidate::from_flat_pat(flat_pat, has_guard);
-                if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] =
-                    &*candidate.match_pairs
-                {
-                    candidate.subcandidates = self.create_or_subcandidates(pats, has_guard);
-                    let first_match_pair = candidate.match_pairs.pop().unwrap();
-                    candidate.or_span = Some(first_match_pair.pattern.span);
-                }
-                candidate
-            })
-            .collect()
+        pats.iter().cloned().map(|flat_pat| Candidate::from_flat_pat(flat_pat, has_guard)).collect()
     }
 }

From 20b12c2bacb2bba46429806907543a7530a37f8a Mon Sep 17 00:00:00 2001
From: dfireBird 
Date: Wed, 27 Mar 2024 23:00:08 +0530
Subject: [PATCH 105/192] fix lifetime length are not added in count of params
 in highlight

---
 crates/hir-ty/src/method_resolution.rs      |  6 ++++--
 crates/ide/src/syntax_highlighting/tests.rs | 17 +++++++++++++++++
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 064d317835fd..6f52a60e9259 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -644,7 +644,8 @@ pub fn is_dyn_method(
     let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
         return None;
     };
-    let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
+    let generic_params = db.generic_params(trait_id.into());
+    let trait_params = generic_params.type_or_consts.len() + generic_params.lifetimes.len();
     let fn_params = fn_subst.len(Interner) - trait_params;
     let trait_ref = TraitRef {
         trait_id: to_chalk_trait_id(trait_id),
@@ -686,7 +687,8 @@ pub(crate) fn lookup_impl_method_query(
     let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
         return (func, fn_subst);
     };
-    let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
+    let generic_params = db.generic_params(trait_id.into());
+    let trait_params = generic_params.type_or_consts.len() + generic_params.lifetimes.len();
     let fn_params = fn_subst.len(Interner) - trait_params;
     let trait_ref = TraitRef {
         trait_id: to_chalk_trait_id(trait_id),
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 74d3e663d641..c2990fd76eac 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1229,3 +1229,20 @@ fn benchmark_syntax_highlighting_parser() {
     };
     assert_eq!(hash, 1169);
 }
+
+#[test]
+fn highlight_trait_with_lifetimes_regression_16958() {
+    let (analysis, file_id) = fixture::file(
+        r#"
+pub trait Deserialize<'de> {
+    fn deserialize();
+}
+
+fn f<'de, T: Deserialize<'de>>() {
+    T::deserialize();
+}
+"#
+        .trim(),
+    );
+    let _ = analysis.highlight(HL_CONFIG, file_id).unwrap();
+}

From 7410f78e9a3a8a47bea05bb2c52e0ac307712a68 Mon Sep 17 00:00:00 2001
From: Nadrieril 
Date: Tue, 5 Mar 2024 22:10:48 +0100
Subject: [PATCH 106/192] Use `create_or_subcandidates` for all or-pattern
 expansions

---
 .../rustc_mir_build/src/build/matches/mod.rs  | 72 +++++++++----------
 .../src/build/matches/simplify.rs             | 11 +--
 2 files changed, 37 insertions(+), 46 deletions(-)

diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 0fb5ed89437e..067ee40f1985 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1272,9 +1272,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     ) {
         let mut split_or_candidate = false;
         for candidate in &mut *candidates {
-            if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] =
-                &*candidate.match_pairs
-            {
+            if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs {
                 // Split a candidate in which the only match-pair is an or-pattern into multiple
                 // candidates. This is so that
                 //
@@ -1284,9 +1282,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // }
                 //
                 // only generates a single switch.
-                candidate.subcandidates = self.create_or_subcandidates(pats, candidate.has_guard);
-                let first_match_pair = candidate.match_pairs.pop().unwrap();
-                candidate.or_span = Some(first_match_pair.pattern.span);
+                let match_pair = candidate.match_pairs.pop().unwrap();
+                self.create_or_subcandidates(candidate, match_pair);
                 split_or_candidate = true;
             }
         }
@@ -1474,14 +1471,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             return;
         }
 
-        let match_pairs = mem::take(&mut first_candidate.match_pairs);
-        let (first_match_pair, remaining_match_pairs) = match_pairs.split_first().unwrap();
-        let TestCase::Or { ref pats } = &first_match_pair.test_case else { unreachable!() };
-
+        let first_match_pair = first_candidate.match_pairs.remove(0);
+        let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs);
         let remainder_start = self.cfg.start_new_block();
-        let or_span = first_match_pair.pattern.span;
         // Test the alternatives of this or-pattern.
-        self.test_or_pattern(first_candidate, start_block, remainder_start, pats, or_span);
+        self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair);
 
         if !remaining_match_pairs.is_empty() {
             // If more match pairs remain, test them after each subcandidate.
@@ -1516,25 +1510,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         );
     }
 
-    #[instrument(
-        skip(self, start_block, otherwise_block, or_span, candidate, pats),
-        level = "debug"
-    )]
+    #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")]
     fn test_or_pattern<'pat>(
         &mut self,
         candidate: &mut Candidate<'pat, 'tcx>,
         start_block: BasicBlock,
         otherwise_block: BasicBlock,
-        pats: &[FlatPat<'pat, 'tcx>],
-        or_span: Span,
+        match_pair: MatchPair<'pat, 'tcx>,
     ) {
-        debug!("candidate={:#?}\npats={:#?}", candidate, pats);
-        let mut or_candidates: Vec<_> = pats
-            .iter()
-            .cloned()
-            .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
-            .collect();
-        let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
+        let or_span = match_pair.pattern.span;
+        self.create_or_subcandidates(candidate, match_pair);
+        let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect();
         self.match_candidates(
             or_span,
             or_span,
@@ -1542,26 +1528,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             otherwise_block,
             &mut or_candidate_refs,
         );
-        candidate.subcandidates = or_candidates;
-        candidate.or_span = Some(or_span);
         self.merge_trivial_subcandidates(candidate);
     }
 
-    /// Try to merge all of the subcandidates of the given candidate into one.
-    /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`.
+    /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
+    /// subcandidate. Any candidate that has been expanded that way should be passed to
+    /// `merge_trivial_subcandidates` after its subcandidates have been processed.
+    fn create_or_subcandidates<'pat>(
+        &mut self,
+        candidate: &mut Candidate<'pat, 'tcx>,
+        match_pair: MatchPair<'pat, 'tcx>,
+    ) {
+        let TestCase::Or { pats } = match_pair.test_case else { bug!() };
+        debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats);
+        candidate.or_span = Some(match_pair.pattern.span);
+        candidate.subcandidates = pats
+            .into_vec()
+            .into_iter()
+            .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
+            .collect();
+    }
+
+    /// Try to merge all of the subcandidates of the given candidate into one. This avoids
+    /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The or-pattern should have
+    /// been expanded with `create_or_subcandidates`.
     fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
         if candidate.subcandidates.is_empty() || candidate.has_guard {
             // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
             return;
         }
 
-        let mut can_merge = true;
-        for subcandidate in &mut candidate.subcandidates {
-            // FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
-            can_merge &=
-                subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty();
-        }
-
+        // FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
+        let can_merge = candidate.subcandidates.iter().all(|subcandidate| {
+            subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty()
+        });
         if can_merge {
             let any_matches = self.cfg.start_new_block();
             let or_span = candidate.or_span.take().unwrap();
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs
index 0c4d45ea0504..90f12e55ff4c 100644
--- a/compiler/rustc_mir_build/src/build/matches/simplify.rs
+++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs
@@ -12,7 +12,7 @@
 //! sort of test: for example, testing which variant an enum is, or
 //! testing a value against a constant.
 
-use crate::build::matches::{Candidate, FlatPat, MatchPair, PatternExtraData, TestCase};
+use crate::build::matches::{MatchPair, PatternExtraData, TestCase};
 use crate::build::Builder;
 
 use std::mem;
@@ -66,13 +66,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. }));
         debug!(simplified = ?match_pairs, "simplify_match_pairs");
     }
-
-    /// Create a new candidate for each pattern in `pats`.
-    pub(super) fn create_or_subcandidates<'pat>(
-        &mut self,
-        pats: &[FlatPat<'pat, 'tcx>],
-        has_guard: bool,
-    ) -> Vec> {
-        pats.iter().cloned().map(|flat_pat| Candidate::from_flat_pat(flat_pat, has_guard)).collect()
-    }
 }

From 662ea73e4e2df83de04c4f2936c3db9eb3a1e24c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Thu, 28 Mar 2024 07:57:53 +0200
Subject: [PATCH 107/192] Revert debug extension priorities

---
 editors/code/src/debug.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index 3fc63fc7d81a..cd7e7c35e992 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -81,8 +81,8 @@ async function getDebugConfiguration(
     if (!editor) return;
 
     const knownEngines: Record = {
-        "ms-vscode.cpptools": getCCppDebugConfig,
         "vadimcn.vscode-lldb": getCodeLldbDebugConfig,
+        "ms-vscode.cpptools": getCCppDebugConfig,
         "webfreak.debug": getNativeDebugConfig,
     };
     const debugOptions = ctx.config.debug;

From 3521089985951f684d33235dd0a5f99bbb3140f9 Mon Sep 17 00:00:00 2001
From: roife 
Date: Thu, 28 Mar 2024 14:25:26 +0800
Subject: [PATCH 108/192] fix: use lldb when debugging with C++ extension

---
 editors/code/src/debug.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index cd7e7c35e992..bad1f48de855 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -203,6 +203,10 @@ function getCCppDebugConfig(
         cwd: cargoWorkspace || runnable.args.workspaceRoot,
         sourceFileMap,
         env,
+        // See https://github.com/rust-lang/rust-analyzer/issues/16901#issuecomment-2024486941
+        osx: {
+            MIMode: "lldb",
+        },
     };
 }
 

From 34a8cd6a7ab58d6d53d9c3af1fed2f4cc18a9e0e Mon Sep 17 00:00:00 2001
From: dfireBird 
Date: Thu, 28 Mar 2024 20:31:15 +0530
Subject: [PATCH 109/192] fix ADT hover considering only type or const len not
 lifetimes

---
 crates/hir/src/lib.rs         |  3 ++-
 crates/ide/src/hover/tests.rs | 22 ++++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b4388ad9656f..cf6297c03701 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1418,7 +1418,8 @@ impl Adt {
     }
 
     pub fn layout(self, db: &dyn HirDatabase) -> Result {
-        if db.generic_params(self.into()).iter().count() != 0 {
+        let generic_params = &db.generic_params(self.into());
+        if generic_params.iter().next().is_some() || generic_params.iter_lt().next().is_some() {
             return Err(LayoutError::HasPlaceholder);
         }
         let krate = self.krate(db).id;
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 192f6c0272ba..08925fcdff58 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -7856,3 +7856,25 @@ impl Iterator for S {
         "#]],
     );
 }
+
+#[test]
+fn hover_lifetime_regression_16963() {
+    check(
+        r#"
+struct Pedro$0<'a> {
+    hola: &'a str
+}
+"#,
+        expect![[r#"
+            *Pedro*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            struct Pedro<'a>
+            ```
+        "#]],
+    )
+}

From fd7909aa59a761f8ba5bf34df3d84a6afa4bff1c Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Thu, 28 Mar 2024 22:46:07 +0100
Subject: [PATCH 110/192] x.py test miri: respect --no-doc / --doc

---
 src/bootstrap/src/core/build_steps/test.rs | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 48596af7ca02..11baf0cda80b 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -680,12 +680,22 @@ impl Step for Miri {
             .arg("--manifest-path")
             .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml"));
         cargo.arg("--target").arg(target.rustc_target_arg());
-        cargo.arg("--").args(builder.config.test_args());
 
         // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "run", not a "test".
         // Also, we want the rustdoc from the "next" stage for the same reason that we build a std from the next stage.
         // So let's just set that here, and bypass bootstrap's RUSTDOC (just like cargo-miri already ignores bootstrap's RUSTC_WRAPPER).
-        cargo.env("RUSTDOC", builder.rustdoc(compiler_std));
+        if builder.doc_tests != DocTests::No {
+            cargo.env("RUSTDOC", builder.rustdoc(compiler_std));
+        }
+        match builder.doc_tests {
+            DocTests::Yes => {}
+            DocTests::No => {
+                cargo.arg("--tests");
+            }
+            DocTests::Only => {
+                cargo.arg("--doc");
+            }
+        }
 
         // Tell `cargo miri` where to find things.
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
@@ -694,6 +704,8 @@ impl Step for Miri {
         // Debug things.
         cargo.env("RUST_BACKTRACE", "1");
 
+        // Finally, pass test-args and run everything.
+        cargo.arg("--").args(builder.config.test_args());
         let mut cargo = Command::from(cargo);
         {
             let _time = helpers::timeit(builder);

From beec6914c84b1f7459847d0226086759e63b3f2c Mon Sep 17 00:00:00 2001
From: hkalbasi 
Date: Fri, 29 Mar 2024 05:34:43 +0330
Subject: [PATCH 111/192] Resolve tests per file instead of per crate in test
 explorer

---
 crates/ide/src/lib.rs                        |  4 ++
 crates/ide/src/test_explorer.rs              | 68 ++++++++++++++++++--
 crates/rust-analyzer/src/handlers/request.rs |  8 ++-
 crates/rust-analyzer/src/lsp/ext.rs          |  3 +-
 crates/rust-analyzer/src/main_loop.rs        | 25 ++++---
 docs/dev/lsp-extensions.md                   |  8 ++-
 editors/code/src/lsp_ext.ts                  |  6 +-
 editors/code/src/test_explorer.ts            | 62 +++++++++++++-----
 8 files changed, 143 insertions(+), 41 deletions(-)

diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 7a6c6ec41d0e..ad48d803895f 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -353,6 +353,10 @@ impl Analysis {
         self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id))
     }
 
+    pub fn discover_tests_in_file(&self, file_id: FileId) -> Cancellable> {
+        self.with_db(|db| test_explorer::discover_tests_in_file(db, file_id))
+    }
+
     /// Renders the crate graph to GraphViz "dot" syntax.
     pub fn view_crate_graph(&self, full: bool) -> Cancellable> {
         self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
diff --git a/crates/ide/src/test_explorer.rs b/crates/ide/src/test_explorer.rs
index ca4713997030..99e243086079 100644
--- a/crates/ide/src/test_explorer.rs
+++ b/crates/ide/src/test_explorer.rs
@@ -7,7 +7,7 @@ use ide_db::{
 };
 use syntax::TextRange;
 
-use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav};
+use crate::{runnables::runnable_fn, NavigationTarget, Runnable, TryToNav};
 
 #[derive(Debug)]
 pub enum TestItemKind {
@@ -56,7 +56,12 @@ fn find_crate_by_id(crate_graph: &CrateGraph, crate_id: &str) -> Option
     })
 }
 
-fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String) -> Vec {
+fn discover_tests_in_module(
+    db: &RootDatabase,
+    module: Module,
+    prefix_id: String,
+    only_in_this_file: bool,
+) -> Vec {
     let sema = Semantics::new(db);
 
     let mut r = vec![];
@@ -64,9 +69,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String
         let module_name =
             c.name(db).as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]").to_owned();
         let module_id = format!("{prefix_id}::{module_name}");
-        let module_children = discover_tests_in_module(db, c, module_id.clone());
+        let module_children = discover_tests_in_module(db, c, module_id.clone(), only_in_this_file);
         if !module_children.is_empty() {
-            let nav = c.to_nav(db).call_site;
+            let nav = NavigationTarget::from_module_to_decl(sema.db, c).call_site;
             r.push(TestItem {
                 id: module_id,
                 kind: TestItemKind::Module,
@@ -76,7 +81,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String
                 text_range: Some(nav.focus_or_full_range()),
                 runnable: None,
             });
-            r.extend(module_children);
+            if !only_in_this_file || c.is_inline(db) {
+                r.extend(module_children);
+            }
         }
     }
     for def in module.declarations(db) {
@@ -112,6 +119,55 @@ pub(crate) fn discover_tests_in_crate_by_test_id(
     discover_tests_in_crate(db, crate_id)
 }
 
+pub(crate) fn discover_tests_in_file(db: &RootDatabase, file_id: FileId) -> Vec {
+    let sema = Semantics::new(db);
+
+    let Some(module) = sema.file_to_module_def(file_id) else { return vec![] };
+    let Some((mut tests, id)) = find_module_id_and_test_parents(&sema, module) else {
+        return vec![];
+    };
+    tests.extend(discover_tests_in_module(db, module, id, true));
+    tests
+}
+
+fn find_module_id_and_test_parents(
+    sema: &Semantics<'_, RootDatabase>,
+    module: Module,
+) -> Option<(Vec, String)> {
+    let Some(parent) = module.parent(sema.db) else {
+        let name = module.krate().display_name(sema.db)?.to_string();
+        return Some((
+            vec![TestItem {
+                id: name.clone(),
+                kind: TestItemKind::Crate(module.krate().into()),
+                label: name.clone(),
+                parent: None,
+                file: None,
+                text_range: None,
+                runnable: None,
+            }],
+            name,
+        ));
+    };
+    let (mut r, mut id) = find_module_id_and_test_parents(sema, parent)?;
+    let parent = Some(id.clone());
+    id += "::";
+    let module_name = &module.name(sema.db);
+    let module_name = module_name.as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]");
+    id += module_name;
+    let nav = NavigationTarget::from_module_to_decl(sema.db, module).call_site;
+    r.push(TestItem {
+        id: id.clone(),
+        kind: TestItemKind::Module,
+        label: module_name.to_owned(),
+        parent,
+        file: Some(nav.file_id),
+        text_range: Some(nav.focus_or_full_range()),
+        runnable: None,
+    });
+    Some((r, id))
+}
+
 pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> Vec {
     let crate_graph = db.crate_graph();
     if !crate_graph[crate_id].origin.is_local() {
@@ -133,6 +189,6 @@ pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> V
         text_range: None,
         runnable: None,
     }];
-    r.extend(discover_tests_in_module(db, module, crate_test_id));
+    r.extend(discover_tests_in_module(db, module, crate_test_id, false));
     r
 }
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 4d4103a7ad7a..77692ed3ae76 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -238,9 +238,12 @@ pub(crate) fn handle_discover_test(
     let (tests, scope) = match params.test_id {
         Some(id) => {
             let crate_id = id.split_once("::").map(|it| it.0).unwrap_or(&id);
-            (snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?, vec![crate_id.to_owned()])
+            (
+                snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?,
+                Some(vec![crate_id.to_owned()]),
+            )
         }
-        None => (snap.analysis.discover_test_roots()?, vec![]),
+        None => (snap.analysis.discover_test_roots()?, None),
     };
     for t in &tests {
         hack_recover_crate_name::insert_name(t.id.clone());
@@ -254,6 +257,7 @@ pub(crate) fn handle_discover_test(
             })
             .collect(),
         scope,
+        scope_file: None,
     })
 }
 
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index 5d3d75efcba4..eac982f1b27b 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -194,7 +194,8 @@ pub struct TestItem {
 #[serde(rename_all = "camelCase")]
 pub struct DiscoverTestResults {
     pub tests: Vec,
-    pub scope: Vec,
+    pub scope: Option>,
+    pub scope_file: Option>,
 }
 
 pub enum DiscoverTest {}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 666bf2920e48..38df32351256 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -9,9 +9,8 @@ use std::{
 use always_assert::always;
 use crossbeam_channel::{never, select, Receiver};
 use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
-use itertools::Itertools;
 use lsp_server::{Connection, Notification, Request};
-use lsp_types::notification::Notification as _;
+use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
 use stdx::thread::ThreadIntent;
 use vfs::FileId;
 
@@ -533,22 +532,14 @@ impl GlobalState {
             let snapshot = self.snapshot();
             move || {
                 let tests = subscriptions
-                    .into_iter()
-                    .filter_map(|f| snapshot.analysis.crates_for(f).ok())
-                    .flatten()
-                    .unique()
-                    .filter_map(|c| snapshot.analysis.discover_tests_in_crate(c).ok())
+                    .iter()
+                    .copied()
+                    .filter_map(|f| snapshot.analysis.discover_tests_in_file(f).ok())
                     .flatten()
                     .collect::>();
                 for t in &tests {
                     hack_recover_crate_name::insert_name(t.id.clone());
                 }
-                let scope = tests
-                    .iter()
-                    .filter_map(|t| Some(t.id.split_once("::")?.0))
-                    .unique()
-                    .map(|it| it.to_owned())
-                    .collect();
                 Task::DiscoverTest(lsp_ext::DiscoverTestResults {
                     tests: tests
                         .into_iter()
@@ -557,7 +548,13 @@ impl GlobalState {
                             to_proto::test_item(&snapshot, t, line_index.as_ref())
                         })
                         .collect(),
-                    scope,
+                    scope: None,
+                    scope_file: Some(
+                        subscriptions
+                            .into_iter()
+                            .map(|f| TextDocumentIdentifier { uri: to_proto::url(&snapshot, f) })
+                            .collect(),
+                    ),
                 })
             }
         });
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 8db66687aae3..939b1819c7e8 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
  $DIR/async-is-unwindsafe.rs:25:18
+  --> $DIR/async-is-unwindsafe.rs:26:18
    |
 LL |         let cx_ref = &mut cx;
    |             ------ has type `&mut Context<'_>` which does not implement `UnwindSafe`
@@ -31,6 +30,38 @@ note: required by a bound in `is_unwindsafe`
 LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {}
    |                          ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe`
 
-error: aborting due to 1 previous error
+error[E0277]: the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary
+  --> $DIR/async-is-unwindsafe.rs:12:5
+   |
+LL |        is_unwindsafe(async {
+   |   _____^_____________-
+   |  |_____|
+   | ||
+LL | ||
+LL | ||
+LL | ||         use std::ptr::null;
+...  ||
+LL | ||         drop(cx_ref);
+LL | ||     });
+   | ||_____-^ `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary
+   |  |_____|
+   |        within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`
+   |
+   = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut (dyn Any + 'static)`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe`
+note: future does not implement `UnwindSafe` as this value is used across an await
+  --> $DIR/async-is-unwindsafe.rs:26:18
+   |
+LL |         let mut cx = Context::from_waker(&waker);
+   |             ------ has type `Context<'_>` which does not implement `UnwindSafe`
+...
+LL |         async {}.await; // this needs an inner await point
+   |                  ^^^^^ await occurs here, with `mut cx` maybe used later
+note: required by a bound in `is_unwindsafe`
+  --> $DIR/async-is-unwindsafe.rs:3:26
+   |
+LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {}
+   |                          ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe`
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0277`.

From 0601f0c66d4ea250193a64a214988da425c3a47d Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sat, 30 Mar 2024 00:36:45 -0700
Subject: [PATCH 119/192] De-LLVM the unchecked shifts [MCP#693]

This is just one part of the MCP, but it's the one that IMHO removes the most noise from the standard library code.

Seems net simpler this way, since MIR already supported heterogeneous shifts anyway, and thus it's not more work for backends than before.
---
 compiler/rustc_codegen_ssa/src/base.rs        |  32 ++-
 compiler/rustc_codegen_ssa/src/common.rs      |  41 +---
 compiler/rustc_codegen_ssa/src/mir/rvalue.rs  |  12 +-
 .../rustc_hir_analysis/src/check/intrinsic.rs |   5 +-
 library/core/src/intrinsics.rs                |   6 +-
 library/core/src/num/int_macros.rs            |  32 ++-
 library/core/src/num/mod.rs                   |  11 --
 library/core/src/num/uint_macros.rs           |  32 ++-
 library/core/src/ptr/mod.rs                   |  14 +-
 tests/codegen/unchecked_shifts.rs             |  49 ++++-
 tests/mir-opt/inline/unchecked_shifts.rs      |  19 +-
 ...hl_unsigned_bigger.Inline.panic-abort.diff |  36 ----
 ...l_unsigned_bigger.Inline.panic-unwind.diff |  36 ----
 ...ed_bigger.PreCodegen.after.panic-abort.mir |  22 ---
 ...d_bigger.PreCodegen.after.panic-unwind.mir |  22 ---
 ...l_unsigned_smaller.Inline.panic-abort.diff |  11 +-
 ..._unsigned_smaller.Inline.panic-unwind.diff |  11 +-
 ...d_smaller.PreCodegen.after.panic-abort.mir |  11 +-
 ..._smaller.PreCodegen.after.panic-unwind.mir |  11 +-
 ..._shr_signed_bigger.Inline.panic-abort.diff |   6 +-
 ...shr_signed_bigger.Inline.panic-unwind.diff |   6 +-
 ...ed_bigger.PreCodegen.after.panic-abort.mir |   6 +-
 ...d_bigger.PreCodegen.after.panic-unwind.mir |   6 +-
 ...shr_signed_smaller.Inline.panic-abort.diff |  41 ----
 ...hr_signed_smaller.Inline.panic-unwind.diff |  41 ----
 ...d_smaller.PreCodegen.after.panic-abort.mir |  27 ---
 ..._smaller.PreCodegen.after.panic-unwind.mir |  27 ---
 tests/mir-opt/lower_intrinsics.rs             |   6 +-
 ...unchecked.LowerIntrinsics.panic-abort.diff | 183 +++++++++++-------
 ...nchecked.LowerIntrinsics.panic-unwind.diff | 183 +++++++++++-------
 tests/ui/consts/const-int-unchecked.rs        |  12 +-
 tests/ui/consts/const-int-unchecked.stderr    |  24 +--
 32 files changed, 405 insertions(+), 576 deletions(-)
 delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff
 delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff
 delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir
 delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir
 delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
 delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
 delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
 delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir

diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index f7f2bfca838e..ec21a7cee477 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -5,7 +5,7 @@ use crate::back::write::{
     compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm,
     submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen,
 };
-use crate::common::{IntPredicate, RealPredicate, TypeKind};
+use crate::common::{self, IntPredicate, RealPredicate, TypeKind};
 use crate::errors;
 use crate::meth;
 use crate::mir;
@@ -33,7 +33,7 @@ use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
-use rustc_session::config::{self, CrateType, EntryFnType, OutputType};
+use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
 use rustc_session::Session;
 use rustc_span::symbol::sym;
 use rustc_span::Symbol;
@@ -300,14 +300,32 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     }
 }
 
-pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+/// Shifts in MIR are all allowed to have mismatched LHS & RHS types.
+///
+/// This does all the appropriate conversions needed to pass it to the builder's
+/// shift methods, which are UB for out-of-range shifts.
+///
+/// If `is_unchecked` is false, this masks the RHS to ensure it stays in-bounds.
+/// For 32- and 64-bit types, this matches the semantics
+/// of Java. (See related discussion on #1877 and #10183.)
+///
+/// If `is_unchecked` is true, this does no masking, and adds sufficient `assume`
+/// calls or operation flags to preserve as much freedom to optimize as possible.
+pub fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     lhs: Bx::Value,
-    rhs: Bx::Value,
+    mut rhs: Bx::Value,
+    is_unchecked: bool,
 ) -> Bx::Value {
     // Shifts may have any size int on the rhs
     let mut rhs_llty = bx.cx().val_ty(rhs);
     let mut lhs_llty = bx.cx().val_ty(lhs);
+
+    let mask = common::shift_mask_val(bx, lhs_llty, rhs_llty, false);
+    if !is_unchecked {
+        rhs = bx.and(rhs, mask);
+    }
+
     if bx.cx().type_kind(rhs_llty) == TypeKind::Vector {
         rhs_llty = bx.cx().element_type(rhs_llty)
     }
@@ -317,6 +335,12 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     let rhs_sz = bx.cx().int_width(rhs_llty);
     let lhs_sz = bx.cx().int_width(lhs_llty);
     if lhs_sz < rhs_sz {
+        if is_unchecked && bx.sess().opts.optimize != OptLevel::No {
+            // FIXME: Use `trunc nuw` once that's available
+            let inrange = bx.icmp(IntPredicate::IntULE, rhs, mask);
+            bx.assume(inrange);
+        }
+
         bx.trunc(rhs, lhs_llty)
     } else if lhs_sz > rhs_sz {
         // We zero-extend even if the RHS is signed. So e.g. `(x: i32) << -1i8` will zero-extend the
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index 71fca403defb..b41739867c7d 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -3,10 +3,9 @@
 use rustc_hir::LangItem;
 use rustc_middle::mir;
 use rustc_middle::ty::Instance;
-use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt};
+use rustc_middle::ty::{self, layout::TyAndLayout, TyCtxt};
 use rustc_span::Span;
 
-use crate::base;
 use crate::traits::*;
 
 #[derive(Copy, Clone)]
@@ -128,44 +127,6 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance)
 }
 
-// To avoid UB from LLVM, these two functions mask RHS with an
-// appropriate mask unconditionally (i.e., the fallback behavior for
-// all shifts). For 32- and 64-bit types, this matches the semantics
-// of Java. (See related discussion on #1877 and #10183.)
-
-pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
-    bx: &mut Bx,
-    lhs: Bx::Value,
-    rhs: Bx::Value,
-) -> Bx::Value {
-    let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
-    // #1877, #10183: Ensure that input is always valid
-    let rhs = shift_mask_rhs(bx, rhs);
-    bx.shl(lhs, rhs)
-}
-
-pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
-    bx: &mut Bx,
-    lhs_t: Ty<'tcx>,
-    lhs: Bx::Value,
-    rhs: Bx::Value,
-) -> Bx::Value {
-    let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
-    // #1877, #10183: Ensure that input is always valid
-    let rhs = shift_mask_rhs(bx, rhs);
-    let is_signed = lhs_t.is_signed();
-    if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) }
-}
-
-fn shift_mask_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
-    bx: &mut Bx,
-    rhs: Bx::Value,
-) -> Bx::Value {
-    let rhs_llty = bx.val_ty(rhs);
-    let shift_val = shift_mask_val(bx, rhs_llty, rhs_llty, false);
-    bx.and(rhs, shift_val)
-}
-
 pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     llty: Bx::Type,
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 0af84ff067af..4443937bc3ba 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -3,7 +3,7 @@ use super::place::PlaceRef;
 use super::{FunctionCx, LocalRef};
 
 use crate::base;
-use crate::common::{self, IntPredicate};
+use crate::common::IntPredicate;
 use crate::traits::*;
 use crate::MemFlags;
 
@@ -860,14 +860,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.inbounds_gep(llty, lhs, &[rhs])
                 }
             }
-            mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs),
-            mir::BinOp::ShlUnchecked => {
-                let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
+            mir::BinOp::Shl | mir::BinOp::ShlUnchecked => {
+                let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShlUnchecked);
                 bx.shl(lhs, rhs)
             }
-            mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs),
-            mir::BinOp::ShrUnchecked => {
-                let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
+            mir::BinOp::Shr | mir::BinOp::ShrUnchecked => {
+                let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShrUnchecked);
                 if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) }
             }
             mir::BinOp::Ne
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index f482ae4f5fa0..3c58b6704e49 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -454,9 +454,8 @@ pub fn check_intrinsic_type(
             sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
                 (1, 0, vec![param(0), param(0)], param(0))
             }
-            sym::unchecked_shl | sym::unchecked_shr | sym::rotate_left | sym::rotate_right => {
-                (1, 0, vec![param(0), param(0)], param(0))
-            }
+            sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)),
+            sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), param(0)], param(0)),
             sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => {
                 (1, 0, vec![param(0), param(0)], param(0))
             }
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 26e813346be6..e08828f0005e 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2224,18 +2224,20 @@ extern "rust-intrinsic" {
     /// Safe wrappers for this intrinsic are available on the integer
     /// primitives via the `checked_shl` method. For example,
     /// [`u32::checked_shl`]
+    #[cfg(not(bootstrap))]
     #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")]
     #[rustc_nounwind]
-    pub fn unchecked_shl(x: T, y: T) -> T;
+    pub fn unchecked_shl(x: T, y: U) -> T;
     /// Performs an unchecked right shift, resulting in undefined behavior when
     /// `y < 0` or `y >= N`, where N is the width of T in bits.
     ///
     /// Safe wrappers for this intrinsic are available on the integer
     /// primitives via the `checked_shr` method. For example,
     /// [`u32::checked_shr`]
+    #[cfg(not(bootstrap))]
     #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")]
     #[rustc_nounwind]
-    pub fn unchecked_shr(x: T, y: T) -> T;
+    pub fn unchecked_shr(x: T, y: U) -> T;
 
     /// Returns the result of an unchecked addition, resulting in
     /// undefined behavior when `x + y > T::MAX` or `x + y < T::MIN`.
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 2fec8ef23810..36e119dc84c7 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -1253,10 +1253,18 @@ macro_rules! int_impl {
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self {
-            // SAFETY: the caller must uphold the safety contract for
-            // `unchecked_shl`.
-            // Any legal shift amount is losslessly representable in the self type.
-            unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) }
+            #[cfg(bootstrap)]
+            {
+                // For bootstrapping, just use built-in primitive shift.
+                // panicking is a legal manifestation of UB
+                self << rhs
+            }
+            #[cfg(not(bootstrap))]
+            {
+                // SAFETY: the caller must uphold the safety contract for
+                // `unchecked_shl`.
+                unsafe { intrinsics::unchecked_shl(self, rhs) }
+            }
         }
 
         /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is
@@ -1336,10 +1344,18 @@ macro_rules! int_impl {
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
-            // SAFETY: the caller must uphold the safety contract for
-            // `unchecked_shr`.
-            // Any legal shift amount is losslessly representable in the self type.
-            unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) }
+            #[cfg(bootstrap)]
+            {
+                // For bootstrapping, just use built-in primitive shift.
+                // panicking is a legal manifestation of UB
+                self >> rhs
+            }
+            #[cfg(not(bootstrap))]
+            {
+                // SAFETY: the caller must uphold the safety contract for
+                // `unchecked_shr`.
+                unsafe { intrinsics::unchecked_shr(self, rhs) }
+            }
         }
 
         /// Checked absolute value. Computes `self.abs()`, returning `None` if
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 03c977abbbb4..92f4bc922e94 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -286,17 +286,6 @@ macro_rules! widening_impl {
     };
 }
 
-macro_rules! conv_rhs_for_unchecked_shift {
-    ($SelfT:ty, $x:expr) => {{
-        // If the `as` cast will truncate, ensure we still tell the backend
-        // that the pre-truncation value was also small.
-        if <$SelfT>::BITS < 32 {
-            intrinsics::assume($x <= (<$SelfT>::MAX as u32));
-        }
-        $x as $SelfT
-    }};
-}
-
 impl i8 {
     int_impl! {
         Self = i8,
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index f76f110fc4e3..b809e8278d4b 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -1313,10 +1313,18 @@ macro_rules! uint_impl {
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self {
-            // SAFETY: the caller must uphold the safety contract for
-            // `unchecked_shl`.
-            // Any legal shift amount is losslessly representable in the self type.
-            unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) }
+            #[cfg(bootstrap)]
+            {
+                // For bootstrapping, just use built-in primitive shift.
+                // panicking is a legal manifestation of UB
+                self << rhs
+            }
+            #[cfg(not(bootstrap))]
+            {
+                // SAFETY: the caller must uphold the safety contract for
+                // `unchecked_shl`.
+                unsafe { intrinsics::unchecked_shl(self, rhs) }
+            }
         }
 
         /// Checked shift right. Computes `self >> rhs`, returning `None`
@@ -1396,10 +1404,18 @@ macro_rules! uint_impl {
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
-            // SAFETY: the caller must uphold the safety contract for
-            // `unchecked_shr`.
-            // Any legal shift amount is losslessly representable in the self type.
-            unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) }
+            #[cfg(bootstrap)]
+            {
+                // For bootstrapping, just use built-in primitive shift.
+                // panicking is a legal manifestation of UB
+                self >> rhs
+            }
+            #[cfg(not(bootstrap))]
+            {
+                // SAFETY: the caller must uphold the safety contract for
+                // `unchecked_shr`.
+                unsafe { intrinsics::unchecked_shr(self, rhs) }
+            }
         }
 
         /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 56378b437e7e..9514f431769e 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -1781,9 +1781,19 @@ pub(crate) const unsafe fn align_offset(p: *const T, a: usize) -> usiz
     // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <=
     // 1, where the method versions of these operations are not inlined.
     use intrinsics::{
-        assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_shl,
-        unchecked_shr, unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub,
+        assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_sub,
+        wrapping_add, wrapping_mul, wrapping_sub,
     };
+    #[cfg(bootstrap)]
+    const unsafe fn unchecked_shl(value: usize, shift: usize) -> usize {
+        value << shift
+    }
+    #[cfg(bootstrap)]
+    const unsafe fn unchecked_shr(value: usize, shift: usize) -> usize {
+        value >> shift
+    }
+    #[cfg(not(bootstrap))]
+    use intrinsics::{unchecked_shl, unchecked_shr};
 
     /// Calculate multiplicative modular inverse of `x` modulo `m`.
     ///
diff --git a/tests/codegen/unchecked_shifts.rs b/tests/codegen/unchecked_shifts.rs
index 9cf2f2b0cb67..7d020fbb4d25 100644
--- a/tests/codegen/unchecked_shifts.rs
+++ b/tests/codegen/unchecked_shifts.rs
@@ -2,6 +2,7 @@
 
 #![crate_type = "lib"]
 #![feature(unchecked_shifts)]
+#![feature(core_intrinsics)]
 
 // CHECK-LABEL: @unchecked_shl_unsigned_same
 #[no_mangle]
@@ -19,7 +20,7 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 {
     // This uses -DAG to avoid failing on irrelevant reorderings,
     // like emitting the truncation earlier.
 
-    // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i32 %b, 65536
+    // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i32 %b, 16
     // CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]])
     // CHECK-DAG: %[[TRUNC:.+]] = trunc i32 %b to i16
     // CHECK-DAG: shl i16 %a, %[[TRUNC]]
@@ -51,7 +52,7 @@ pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 {
     // This uses -DAG to avoid failing on irrelevant reorderings,
     // like emitting the truncation earlier.
 
-    // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i32 %b, 32768
+    // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i32 %b, 16
     // CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]])
     // CHECK-DAG: %[[TRUNC:.+]] = trunc i32 %b to i16
     // CHECK-DAG: ashr i16 %a, %[[TRUNC]]
@@ -66,3 +67,47 @@ pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 {
     // CHECK: ashr i64 %a, %[[EXT]]
     a.unchecked_shr(b)
 }
+
+// CHECK-LABEL: @unchecked_shr_u128_i8
+#[no_mangle]
+pub unsafe fn unchecked_shr_u128_i8(a: u128, b: i8) -> u128 {
+    // CHECK-NOT: assume
+    // CHECK: %[[EXT:.+]] = zext{{( nneg)?}} i8 %b to i128
+    // CHECK: lshr i128 %a, %[[EXT]]
+    std::intrinsics::unchecked_shr(a, b)
+}
+
+// CHECK-LABEL: @unchecked_shl_i128_u8
+#[no_mangle]
+pub unsafe fn unchecked_shl_i128_u8(a: i128, b: u8) -> i128 {
+    // CHECK-NOT: assume
+    // CHECK: %[[EXT:.+]] = zext{{( nneg)?}} i8 %b to i128
+    // CHECK: shl i128 %a, %[[EXT]]
+    std::intrinsics::unchecked_shl(a, b)
+}
+
+// CHECK-LABEL: @unchecked_shl_u8_i128
+#[no_mangle]
+pub unsafe fn unchecked_shl_u8_i128(a: u8, b: i128) -> u8 {
+    // This uses -DAG to avoid failing on irrelevant reorderings,
+    // like emitting the truncation earlier.
+
+    // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i128 %b, 8
+    // CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]])
+    // CHECK-DAG: %[[TRUNC:.+]] = trunc i128 %b to i8
+    // CHECK-DAG: shl i8 %a, %[[TRUNC]]
+    std::intrinsics::unchecked_shl(a, b)
+}
+
+// CHECK-LABEL: @unchecked_shr_i8_u128
+#[no_mangle]
+pub unsafe fn unchecked_shr_i8_u128(a: i8, b: u128) -> i8 {
+    // This uses -DAG to avoid failing on irrelevant reorderings,
+    // like emitting the truncation earlier.
+
+    // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i128 %b, 8
+    // CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]])
+    // CHECK-DAG: %[[TRUNC:.+]] = trunc i128 %b to i8
+    // CHECK-DAG: ashr i8 %a, %[[TRUNC]]
+    std::intrinsics::unchecked_shr(a, b)
+}
diff --git a/tests/mir-opt/inline/unchecked_shifts.rs b/tests/mir-opt/inline/unchecked_shifts.rs
index 12b00e76a11b..1e54f28725d8 100644
--- a/tests/mir-opt/inline/unchecked_shifts.rs
+++ b/tests/mir-opt/inline/unchecked_shifts.rs
@@ -4,6 +4,9 @@
 
 //@ compile-flags: -Zmir-opt-level=2 -Zinline-mir
 
+// These used to be more interesting when the library had to fix the RHS type.
+// After MCP#693, though, that's the backend's probablem, not something in MIR.
+
 // EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.diff
 // EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.mir
 pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 {
@@ -12,22 +15,6 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 {
     a.unchecked_shl(b)
 }
 
-// EMIT_MIR unchecked_shifts.unchecked_shr_signed_smaller.Inline.diff
-// EMIT_MIR unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.mir
-pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 {
-    // CHECK-LABEL: fn unchecked_shr_signed_smaller(
-    // CHECK: (inlined core::num::::unchecked_shr)
-    a.unchecked_shr(b)
-}
-
-// EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.diff
-// EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.mir
-pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 {
-    // CHECK-LABEL: fn unchecked_shl_unsigned_bigger(
-    // CHECK: (inlined core::num::::unchecked_shl)
-    a.unchecked_shl(b)
-}
-
 // EMIT_MIR unchecked_shifts.unchecked_shr_signed_bigger.Inline.diff
 // EMIT_MIR unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.mir
 pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff
deleted file mode 100644
index 1ab1d01e5fac..000000000000
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff
+++ /dev/null
@@ -1,36 +0,0 @@
-- // MIR for `unchecked_shl_unsigned_bigger` before Inline
-+ // MIR for `unchecked_shl_unsigned_bigger` after Inline
-  
-  fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
-      debug a => _1;
-      debug b => _2;
-      let mut _0: u64;
-      let mut _3: u64;
-      let mut _4: u32;
-+     scope 1 (inlined core::num::::unchecked_shl) {
-+         debug self => _3;
-+         debug rhs => _4;
-+         let mut _5: u64;
-+         scope 2 {
-+         }
-+     }
-  
-      bb0: {
-          StorageLive(_3);
-          _3 = _1;
-          StorageLive(_4);
-          _4 = _2;
--         _0 = core::num::::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable];
--     }
-- 
--     bb1: {
-+         StorageLive(_5);
-+         _5 = _4 as u64 (IntToInt);
-+         _0 = ShlUnchecked(_3, move _5);
-+         StorageDead(_5);
-          StorageDead(_4);
-          StorageDead(_3);
-          return;
-      }
-  }
-  
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff
deleted file mode 100644
index d71b5c4a626e..000000000000
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff
+++ /dev/null
@@ -1,36 +0,0 @@
-- // MIR for `unchecked_shl_unsigned_bigger` before Inline
-+ // MIR for `unchecked_shl_unsigned_bigger` after Inline
-  
-  fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
-      debug a => _1;
-      debug b => _2;
-      let mut _0: u64;
-      let mut _3: u64;
-      let mut _4: u32;
-+     scope 1 (inlined core::num::::unchecked_shl) {
-+         debug self => _3;
-+         debug rhs => _4;
-+         let mut _5: u64;
-+         scope 2 {
-+         }
-+     }
-  
-      bb0: {
-          StorageLive(_3);
-          _3 = _1;
-          StorageLive(_4);
-          _4 = _2;
--         _0 = core::num::::unchecked_shl(move _3, move _4) -> [return: bb1, unwind continue];
--     }
-- 
--     bb1: {
-+         StorageLive(_5);
-+         _5 = _4 as u64 (IntToInt);
-+         _0 = ShlUnchecked(_3, move _5);
-+         StorageDead(_5);
-          StorageDead(_4);
-          StorageDead(_3);
-          return;
-      }
-  }
-  
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir
deleted file mode 100644
index 65b832497f9d..000000000000
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir
+++ /dev/null
@@ -1,22 +0,0 @@
-// MIR for `unchecked_shl_unsigned_bigger` after PreCodegen
-
-fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
-    debug a => _1;
-    debug b => _2;
-    let mut _0: u64;
-    scope 1 (inlined core::num::::unchecked_shl) {
-        debug self => _1;
-        debug rhs => _2;
-        let mut _3: u64;
-        scope 2 {
-        }
-    }
-
-    bb0: {
-        StorageLive(_3);
-        _3 = _2 as u64 (IntToInt);
-        _0 = ShlUnchecked(_1, move _3);
-        StorageDead(_3);
-        return;
-    }
-}
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir
deleted file mode 100644
index 65b832497f9d..000000000000
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir
+++ /dev/null
@@ -1,22 +0,0 @@
-// MIR for `unchecked_shl_unsigned_bigger` after PreCodegen
-
-fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
-    debug a => _1;
-    debug b => _2;
-    let mut _0: u64;
-    scope 1 (inlined core::num::::unchecked_shl) {
-        debug self => _1;
-        debug rhs => _2;
-        let mut _3: u64;
-        scope 2 {
-        }
-    }
-
-    bb0: {
-        StorageLive(_3);
-        _3 = _2 as u64 (IntToInt);
-        _0 = ShlUnchecked(_1, move _3);
-        StorageDead(_3);
-        return;
-    }
-}
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
index d052219661b3..51e506142a9c 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
@@ -10,8 +10,6 @@
 +     scope 1 (inlined core::num::::unchecked_shl) {
 +         debug self => _3;
 +         debug rhs => _4;
-+         let mut _5: u16;
-+         let mut _6: bool;
 +         scope 2 {
 +         }
 +     }
@@ -25,14 +23,7 @@
 -     }
 - 
 -     bb1: {
-+         StorageLive(_5);
-+         StorageLive(_6);
-+         _6 = Le(_4, const 65535_u32);
-+         assume(move _6);
-+         StorageDead(_6);
-+         _5 = _4 as u16 (IntToInt);
-+         _0 = ShlUnchecked(_3, move _5);
-+         StorageDead(_5);
++         _0 = ShlUnchecked(_3, _4);
           StorageDead(_4);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
index 67a5ac2483b6..053ccc077a4c 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
@@ -10,8 +10,6 @@
 +     scope 1 (inlined core::num::::unchecked_shl) {
 +         debug self => _3;
 +         debug rhs => _4;
-+         let mut _5: u16;
-+         let mut _6: bool;
 +         scope 2 {
 +         }
 +     }
@@ -25,14 +23,7 @@
 -     }
 - 
 -     bb1: {
-+         StorageLive(_5);
-+         StorageLive(_6);
-+         _6 = Le(_4, const 65535_u32);
-+         assume(move _6);
-+         StorageDead(_6);
-+         _5 = _4 as u16 (IntToInt);
-+         _0 = ShlUnchecked(_3, move _5);
-+         StorageDead(_5);
++         _0 = ShlUnchecked(_3, _4);
           StorageDead(_4);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
index f9dff62e0c8a..2be887e9b5a3 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
@@ -7,21 +7,12 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 {
     scope 1 (inlined core::num::::unchecked_shl) {
         debug self => _1;
         debug rhs => _2;
-        let mut _3: bool;
-        let mut _4: u16;
         scope 2 {
         }
     }
 
     bb0: {
-        StorageLive(_4);
-        StorageLive(_3);
-        _3 = Le(_2, const 65535_u32);
-        assume(move _3);
-        StorageDead(_3);
-        _4 = _2 as u16 (IntToInt);
-        _0 = ShlUnchecked(_1, move _4);
-        StorageDead(_4);
+        _0 = ShlUnchecked(_1, _2);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
index f9dff62e0c8a..2be887e9b5a3 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
@@ -7,21 +7,12 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 {
     scope 1 (inlined core::num::::unchecked_shl) {
         debug self => _1;
         debug rhs => _2;
-        let mut _3: bool;
-        let mut _4: u16;
         scope 2 {
         }
     }
 
     bb0: {
-        StorageLive(_4);
-        StorageLive(_3);
-        _3 = Le(_2, const 65535_u32);
-        assume(move _3);
-        StorageDead(_3);
-        _4 = _2 as u16 (IntToInt);
-        _0 = ShlUnchecked(_1, move _4);
-        StorageDead(_4);
+        _0 = ShlUnchecked(_1, _2);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff
index 1e83fec4f3d0..816d03b44052 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff
@@ -10,7 +10,6 @@
 +     scope 1 (inlined core::num::::unchecked_shr) {
 +         debug self => _3;
 +         debug rhs => _4;
-+         let mut _5: i64;
 +         scope 2 {
 +         }
 +     }
@@ -24,10 +23,7 @@
 -     }
 - 
 -     bb1: {
-+         StorageLive(_5);
-+         _5 = _4 as i64 (IntToInt);
-+         _0 = ShrUnchecked(_3, move _5);
-+         StorageDead(_5);
++         _0 = ShrUnchecked(_3, _4);
           StorageDead(_4);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff
index 6aafb61dc557..d4121f89bafb 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff
@@ -10,7 +10,6 @@
 +     scope 1 (inlined core::num::::unchecked_shr) {
 +         debug self => _3;
 +         debug rhs => _4;
-+         let mut _5: i64;
 +         scope 2 {
 +         }
 +     }
@@ -24,10 +23,7 @@
 -     }
 - 
 -     bb1: {
-+         StorageLive(_5);
-+         _5 = _4 as i64 (IntToInt);
-+         _0 = ShrUnchecked(_3, move _5);
-+         StorageDead(_5);
++         _0 = ShrUnchecked(_3, _4);
           StorageDead(_4);
           StorageDead(_3);
           return;
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir
index 7524ec4970e4..4696112175e6 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir
@@ -7,16 +7,12 @@ fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
     scope 1 (inlined core::num::::unchecked_shr) {
         debug self => _1;
         debug rhs => _2;
-        let mut _3: i64;
         scope 2 {
         }
     }
 
     bb0: {
-        StorageLive(_3);
-        _3 = _2 as i64 (IntToInt);
-        _0 = ShrUnchecked(_1, move _3);
-        StorageDead(_3);
+        _0 = ShrUnchecked(_1, _2);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir
index 7524ec4970e4..4696112175e6 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir
@@ -7,16 +7,12 @@ fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
     scope 1 (inlined core::num::::unchecked_shr) {
         debug self => _1;
         debug rhs => _2;
-        let mut _3: i64;
         scope 2 {
         }
     }
 
     bb0: {
-        StorageLive(_3);
-        _3 = _2 as i64 (IntToInt);
-        _0 = ShrUnchecked(_1, move _3);
-        StorageDead(_3);
+        _0 = ShrUnchecked(_1, _2);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
deleted file mode 100644
index 15b36b284de5..000000000000
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
+++ /dev/null
@@ -1,41 +0,0 @@
-- // MIR for `unchecked_shr_signed_smaller` before Inline
-+ // MIR for `unchecked_shr_signed_smaller` after Inline
-  
-  fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
-      debug a => _1;
-      debug b => _2;
-      let mut _0: i16;
-      let mut _3: i16;
-      let mut _4: u32;
-+     scope 1 (inlined core::num::::unchecked_shr) {
-+         debug self => _3;
-+         debug rhs => _4;
-+         let mut _5: i16;
-+         let mut _6: bool;
-+         scope 2 {
-+         }
-+     }
-  
-      bb0: {
-          StorageLive(_3);
-          _3 = _1;
-          StorageLive(_4);
-          _4 = _2;
--         _0 = core::num::::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable];
--     }
-- 
--     bb1: {
-+         StorageLive(_5);
-+         StorageLive(_6);
-+         _6 = Le(_4, const 32767_u32);
-+         assume(move _6);
-+         StorageDead(_6);
-+         _5 = _4 as i16 (IntToInt);
-+         _0 = ShrUnchecked(_3, move _5);
-+         StorageDead(_5);
-          StorageDead(_4);
-          StorageDead(_3);
-          return;
-      }
-  }
-  
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
deleted file mode 100644
index 8629f92dbad4..000000000000
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
+++ /dev/null
@@ -1,41 +0,0 @@
-- // MIR for `unchecked_shr_signed_smaller` before Inline
-+ // MIR for `unchecked_shr_signed_smaller` after Inline
-  
-  fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
-      debug a => _1;
-      debug b => _2;
-      let mut _0: i16;
-      let mut _3: i16;
-      let mut _4: u32;
-+     scope 1 (inlined core::num::::unchecked_shr) {
-+         debug self => _3;
-+         debug rhs => _4;
-+         let mut _5: i16;
-+         let mut _6: bool;
-+         scope 2 {
-+         }
-+     }
-  
-      bb0: {
-          StorageLive(_3);
-          _3 = _1;
-          StorageLive(_4);
-          _4 = _2;
--         _0 = core::num::::unchecked_shr(move _3, move _4) -> [return: bb1, unwind continue];
--     }
-- 
--     bb1: {
-+         StorageLive(_5);
-+         StorageLive(_6);
-+         _6 = Le(_4, const 32767_u32);
-+         assume(move _6);
-+         StorageDead(_6);
-+         _5 = _4 as i16 (IntToInt);
-+         _0 = ShrUnchecked(_3, move _5);
-+         StorageDead(_5);
-          StorageDead(_4);
-          StorageDead(_3);
-          return;
-      }
-  }
-  
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
deleted file mode 100644
index 65fa0d956c06..000000000000
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
+++ /dev/null
@@ -1,27 +0,0 @@
-// MIR for `unchecked_shr_signed_smaller` after PreCodegen
-
-fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
-    debug a => _1;
-    debug b => _2;
-    let mut _0: i16;
-    scope 1 (inlined core::num::::unchecked_shr) {
-        debug self => _1;
-        debug rhs => _2;
-        let mut _3: bool;
-        let mut _4: i16;
-        scope 2 {
-        }
-    }
-
-    bb0: {
-        StorageLive(_4);
-        StorageLive(_3);
-        _3 = Le(_2, const 32767_u32);
-        assume(move _3);
-        StorageDead(_3);
-        _4 = _2 as i16 (IntToInt);
-        _0 = ShrUnchecked(_1, move _4);
-        StorageDead(_4);
-        return;
-    }
-}
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir
deleted file mode 100644
index 65fa0d956c06..000000000000
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir
+++ /dev/null
@@ -1,27 +0,0 @@
-// MIR for `unchecked_shr_signed_smaller` after PreCodegen
-
-fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
-    debug a => _1;
-    debug b => _2;
-    let mut _0: i16;
-    scope 1 (inlined core::num::::unchecked_shr) {
-        debug self => _1;
-        debug rhs => _2;
-        let mut _3: bool;
-        let mut _4: i16;
-        scope 2 {
-        }
-    }
-
-    bb0: {
-        StorageLive(_4);
-        StorageLive(_3);
-        _3 = Le(_2, const 32767_u32);
-        assume(move _3);
-        StorageDead(_3);
-        _4 = _2 as i16 (IntToInt);
-        _0 = ShrUnchecked(_1, move _4);
-        StorageDead(_4);
-        return;
-    }
-}
diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs
index 278ddfce1c33..3250294800ac 100644
--- a/tests/mir-opt/lower_intrinsics.rs
+++ b/tests/mir-opt/lower_intrinsics.rs
@@ -16,7 +16,7 @@ pub fn wrapping(a: i32, b: i32) {
 }
 
 // EMIT_MIR lower_intrinsics.unchecked.LowerIntrinsics.diff
-pub unsafe fn unchecked(a: i32, b: i32) {
+pub unsafe fn unchecked(a: i32, b: i32, c: u32) {
     // CHECK-LABEL: fn unchecked(
     // CHECK: {{_.*}} = AddUnchecked(
     // CHECK: {{_.*}} = SubUnchecked(
@@ -25,6 +25,8 @@ pub unsafe fn unchecked(a: i32, b: i32) {
     // CHECK: {{_.*}} = Rem(
     // CHECK: {{_.*}} = ShlUnchecked(
     // CHECK: {{_.*}} = ShrUnchecked(
+    // CHECK: {{_.*}} = ShlUnchecked(
+    // CHECK: {{_.*}} = ShrUnchecked(
     let _a = core::intrinsics::unchecked_add(a, b);
     let _b = core::intrinsics::unchecked_sub(a, b);
     let _c = core::intrinsics::unchecked_mul(a, b);
@@ -32,6 +34,8 @@ pub unsafe fn unchecked(a: i32, b: i32) {
     let _y = core::intrinsics::unchecked_rem(a, b);
     let _i = core::intrinsics::unchecked_shl(a, b);
     let _j = core::intrinsics::unchecked_shr(a, b);
+    let _k = core::intrinsics::unchecked_shl(a, c);
+    let _l = core::intrinsics::unchecked_shr(a, c);
 }
 
 // EMIT_MIR lower_intrinsics.size_of.LowerIntrinsics.diff
diff --git a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff
index dd92b8d6d2ca..3c9694d0370d 100644
--- a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff
+++ b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-abort.diff
@@ -1,45 +1,58 @@
 - // MIR for `unchecked` before LowerIntrinsics
 + // MIR for `unchecked` after LowerIntrinsics
   
-  fn unchecked(_1: i32, _2: i32) -> () {
+  fn unchecked(_1: i32, _2: i32, _3: u32) -> () {
       debug a => _1;
       debug b => _2;
+      debug c => _3;
       let mut _0: ();
-      let _3: i32;
-      let mut _4: i32;
+      let _4: i32;
       let mut _5: i32;
-      let mut _7: i32;
+      let mut _6: i32;
       let mut _8: i32;
-      let mut _10: i32;
+      let mut _9: i32;
       let mut _11: i32;
-      let mut _13: i32;
+      let mut _12: i32;
       let mut _14: i32;
-      let mut _16: i32;
+      let mut _15: i32;
       let mut _17: i32;
-      let mut _19: i32;
+      let mut _18: i32;
       let mut _20: i32;
-      let mut _22: i32;
+      let mut _21: i32;
       let mut _23: i32;
+      let mut _24: i32;
+      let mut _26: i32;
+      let mut _27: u32;
+      let mut _29: i32;
+      let mut _30: u32;
       scope 1 {
-          debug _a => _3;
-          let _6: i32;
+          debug _a => _4;
+          let _7: i32;
           scope 2 {
-              debug _b => _6;
-              let _9: i32;
+              debug _b => _7;
+              let _10: i32;
               scope 3 {
-                  debug _c => _9;
-                  let _12: i32;
+                  debug _c => _10;
+                  let _13: i32;
                   scope 4 {
-                      debug _x => _12;
-                      let _15: i32;
+                      debug _x => _13;
+                      let _16: i32;
                       scope 5 {
-                          debug _y => _15;
-                          let _18: i32;
+                          debug _y => _16;
+                          let _19: i32;
                           scope 6 {
-                              debug _i => _18;
-                              let _21: i32;
+                              debug _i => _19;
+                              let _22: i32;
                               scope 7 {
-                                  debug _j => _21;
+                                  debug _j => _22;
+                                  let _25: i32;
+                                  scope 8 {
+                                      debug _k => _25;
+                                      let _28: i32;
+                                      scope 9 {
+                                          debug _l => _28;
+                                      }
+                                  }
                               }
                           }
                       }
@@ -49,105 +62,133 @@
       }
   
       bb0: {
-          StorageLive(_3);
           StorageLive(_4);
-          _4 = _1;
           StorageLive(_5);
-          _5 = _2;
--         _3 = unchecked_add::(move _4, move _5) -> [return: bb1, unwind unreachable];
-+         _3 = AddUnchecked(move _4, move _5);
+          _5 = _1;
+          StorageLive(_6);
+          _6 = _2;
+-         _4 = unchecked_add::(move _5, move _6) -> [return: bb1, unwind unreachable];
++         _4 = AddUnchecked(move _5, move _6);
 +         goto -> bb1;
       }
   
       bb1: {
+          StorageDead(_6);
           StorageDead(_5);
-          StorageDead(_4);
-          StorageLive(_6);
           StorageLive(_7);
-          _7 = _1;
           StorageLive(_8);
-          _8 = _2;
--         _6 = unchecked_sub::(move _7, move _8) -> [return: bb2, unwind unreachable];
-+         _6 = SubUnchecked(move _7, move _8);
+          _8 = _1;
+          StorageLive(_9);
+          _9 = _2;
+-         _7 = unchecked_sub::(move _8, move _9) -> [return: bb2, unwind unreachable];
++         _7 = SubUnchecked(move _8, move _9);
 +         goto -> bb2;
       }
   
       bb2: {
+          StorageDead(_9);
           StorageDead(_8);
-          StorageDead(_7);
-          StorageLive(_9);
           StorageLive(_10);
-          _10 = _1;
           StorageLive(_11);
-          _11 = _2;
--         _9 = unchecked_mul::(move _10, move _11) -> [return: bb3, unwind unreachable];
-+         _9 = MulUnchecked(move _10, move _11);
+          _11 = _1;
+          StorageLive(_12);
+          _12 = _2;
+-         _10 = unchecked_mul::(move _11, move _12) -> [return: bb3, unwind unreachable];
++         _10 = MulUnchecked(move _11, move _12);
 +         goto -> bb3;
       }
   
       bb3: {
+          StorageDead(_12);
           StorageDead(_11);
-          StorageDead(_10);
-          StorageLive(_12);
           StorageLive(_13);
-          _13 = _1;
           StorageLive(_14);
-          _14 = _2;
--         _12 = unchecked_div::(move _13, move _14) -> [return: bb4, unwind unreachable];
-+         _12 = Div(move _13, move _14);
+          _14 = _1;
+          StorageLive(_15);
+          _15 = _2;
+-         _13 = unchecked_div::(move _14, move _15) -> [return: bb4, unwind unreachable];
++         _13 = Div(move _14, move _15);
 +         goto -> bb4;
       }
   
       bb4: {
+          StorageDead(_15);
           StorageDead(_14);
-          StorageDead(_13);
-          StorageLive(_15);
           StorageLive(_16);
-          _16 = _1;
           StorageLive(_17);
-          _17 = _2;
--         _15 = unchecked_rem::(move _16, move _17) -> [return: bb5, unwind unreachable];
-+         _15 = Rem(move _16, move _17);
+          _17 = _1;
+          StorageLive(_18);
+          _18 = _2;
+-         _16 = unchecked_rem::(move _17, move _18) -> [return: bb5, unwind unreachable];
++         _16 = Rem(move _17, move _18);
 +         goto -> bb5;
       }
   
       bb5: {
+          StorageDead(_18);
           StorageDead(_17);
-          StorageDead(_16);
-          StorageLive(_18);
           StorageLive(_19);
-          _19 = _1;
           StorageLive(_20);
-          _20 = _2;
--         _18 = unchecked_shl::(move _19, move _20) -> [return: bb6, unwind unreachable];
-+         _18 = ShlUnchecked(move _19, move _20);
+          _20 = _1;
+          StorageLive(_21);
+          _21 = _2;
+-         _19 = unchecked_shl::(move _20, move _21) -> [return: bb6, unwind unreachable];
++         _19 = ShlUnchecked(move _20, move _21);
 +         goto -> bb6;
       }
   
       bb6: {
+          StorageDead(_21);
           StorageDead(_20);
-          StorageDead(_19);
-          StorageLive(_21);
           StorageLive(_22);
-          _22 = _1;
           StorageLive(_23);
-          _23 = _2;
--         _21 = unchecked_shr::(move _22, move _23) -> [return: bb7, unwind unreachable];
-+         _21 = ShrUnchecked(move _22, move _23);
+          _23 = _1;
+          StorageLive(_24);
+          _24 = _2;
+-         _22 = unchecked_shr::(move _23, move _24) -> [return: bb7, unwind unreachable];
++         _22 = ShrUnchecked(move _23, move _24);
 +         goto -> bb7;
       }
   
       bb7: {
+          StorageDead(_24);
           StorageDead(_23);
-          StorageDead(_22);
+          StorageLive(_25);
+          StorageLive(_26);
+          _26 = _1;
+          StorageLive(_27);
+          _27 = _3;
+-         _25 = unchecked_shl::(move _26, move _27) -> [return: bb8, unwind unreachable];
++         _25 = ShlUnchecked(move _26, move _27);
++         goto -> bb8;
+      }
+  
+      bb8: {
+          StorageDead(_27);
+          StorageDead(_26);
+          StorageLive(_28);
+          StorageLive(_29);
+          _29 = _1;
+          StorageLive(_30);
+          _30 = _3;
+-         _28 = unchecked_shr::(move _29, move _30) -> [return: bb9, unwind unreachable];
++         _28 = ShrUnchecked(move _29, move _30);
++         goto -> bb9;
+      }
+  
+      bb9: {
+          StorageDead(_30);
+          StorageDead(_29);
           _0 = const ();
-          StorageDead(_21);
-          StorageDead(_18);
-          StorageDead(_15);
-          StorageDead(_12);
-          StorageDead(_9);
-          StorageDead(_6);
-          StorageDead(_3);
+          StorageDead(_28);
+          StorageDead(_25);
+          StorageDead(_22);
+          StorageDead(_19);
+          StorageDead(_16);
+          StorageDead(_13);
+          StorageDead(_10);
+          StorageDead(_7);
+          StorageDead(_4);
           return;
       }
   }
diff --git a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff
index dd92b8d6d2ca..3c9694d0370d 100644
--- a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff
+++ b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.panic-unwind.diff
@@ -1,45 +1,58 @@
 - // MIR for `unchecked` before LowerIntrinsics
 + // MIR for `unchecked` after LowerIntrinsics
   
-  fn unchecked(_1: i32, _2: i32) -> () {
+  fn unchecked(_1: i32, _2: i32, _3: u32) -> () {
       debug a => _1;
       debug b => _2;
+      debug c => _3;
       let mut _0: ();
-      let _3: i32;
-      let mut _4: i32;
+      let _4: i32;
       let mut _5: i32;
-      let mut _7: i32;
+      let mut _6: i32;
       let mut _8: i32;
-      let mut _10: i32;
+      let mut _9: i32;
       let mut _11: i32;
-      let mut _13: i32;
+      let mut _12: i32;
       let mut _14: i32;
-      let mut _16: i32;
+      let mut _15: i32;
       let mut _17: i32;
-      let mut _19: i32;
+      let mut _18: i32;
       let mut _20: i32;
-      let mut _22: i32;
+      let mut _21: i32;
       let mut _23: i32;
+      let mut _24: i32;
+      let mut _26: i32;
+      let mut _27: u32;
+      let mut _29: i32;
+      let mut _30: u32;
       scope 1 {
-          debug _a => _3;
-          let _6: i32;
+          debug _a => _4;
+          let _7: i32;
           scope 2 {
-              debug _b => _6;
-              let _9: i32;
+              debug _b => _7;
+              let _10: i32;
               scope 3 {
-                  debug _c => _9;
-                  let _12: i32;
+                  debug _c => _10;
+                  let _13: i32;
                   scope 4 {
-                      debug _x => _12;
-                      let _15: i32;
+                      debug _x => _13;
+                      let _16: i32;
                       scope 5 {
-                          debug _y => _15;
-                          let _18: i32;
+                          debug _y => _16;
+                          let _19: i32;
                           scope 6 {
-                              debug _i => _18;
-                              let _21: i32;
+                              debug _i => _19;
+                              let _22: i32;
                               scope 7 {
-                                  debug _j => _21;
+                                  debug _j => _22;
+                                  let _25: i32;
+                                  scope 8 {
+                                      debug _k => _25;
+                                      let _28: i32;
+                                      scope 9 {
+                                          debug _l => _28;
+                                      }
+                                  }
                               }
                           }
                       }
@@ -49,105 +62,133 @@
       }
   
       bb0: {
-          StorageLive(_3);
           StorageLive(_4);
-          _4 = _1;
           StorageLive(_5);
-          _5 = _2;
--         _3 = unchecked_add::(move _4, move _5) -> [return: bb1, unwind unreachable];
-+         _3 = AddUnchecked(move _4, move _5);
+          _5 = _1;
+          StorageLive(_6);
+          _6 = _2;
+-         _4 = unchecked_add::(move _5, move _6) -> [return: bb1, unwind unreachable];
++         _4 = AddUnchecked(move _5, move _6);
 +         goto -> bb1;
       }
   
       bb1: {
+          StorageDead(_6);
           StorageDead(_5);
-          StorageDead(_4);
-          StorageLive(_6);
           StorageLive(_7);
-          _7 = _1;
           StorageLive(_8);
-          _8 = _2;
--         _6 = unchecked_sub::(move _7, move _8) -> [return: bb2, unwind unreachable];
-+         _6 = SubUnchecked(move _7, move _8);
+          _8 = _1;
+          StorageLive(_9);
+          _9 = _2;
+-         _7 = unchecked_sub::(move _8, move _9) -> [return: bb2, unwind unreachable];
++         _7 = SubUnchecked(move _8, move _9);
 +         goto -> bb2;
       }
   
       bb2: {
+          StorageDead(_9);
           StorageDead(_8);
-          StorageDead(_7);
-          StorageLive(_9);
           StorageLive(_10);
-          _10 = _1;
           StorageLive(_11);
-          _11 = _2;
--         _9 = unchecked_mul::(move _10, move _11) -> [return: bb3, unwind unreachable];
-+         _9 = MulUnchecked(move _10, move _11);
+          _11 = _1;
+          StorageLive(_12);
+          _12 = _2;
+-         _10 = unchecked_mul::(move _11, move _12) -> [return: bb3, unwind unreachable];
++         _10 = MulUnchecked(move _11, move _12);
 +         goto -> bb3;
       }
   
       bb3: {
+          StorageDead(_12);
           StorageDead(_11);
-          StorageDead(_10);
-          StorageLive(_12);
           StorageLive(_13);
-          _13 = _1;
           StorageLive(_14);
-          _14 = _2;
--         _12 = unchecked_div::(move _13, move _14) -> [return: bb4, unwind unreachable];
-+         _12 = Div(move _13, move _14);
+          _14 = _1;
+          StorageLive(_15);
+          _15 = _2;
+-         _13 = unchecked_div::(move _14, move _15) -> [return: bb4, unwind unreachable];
++         _13 = Div(move _14, move _15);
 +         goto -> bb4;
       }
   
       bb4: {
+          StorageDead(_15);
           StorageDead(_14);
-          StorageDead(_13);
-          StorageLive(_15);
           StorageLive(_16);
-          _16 = _1;
           StorageLive(_17);
-          _17 = _2;
--         _15 = unchecked_rem::(move _16, move _17) -> [return: bb5, unwind unreachable];
-+         _15 = Rem(move _16, move _17);
+          _17 = _1;
+          StorageLive(_18);
+          _18 = _2;
+-         _16 = unchecked_rem::(move _17, move _18) -> [return: bb5, unwind unreachable];
++         _16 = Rem(move _17, move _18);
 +         goto -> bb5;
       }
   
       bb5: {
+          StorageDead(_18);
           StorageDead(_17);
-          StorageDead(_16);
-          StorageLive(_18);
           StorageLive(_19);
-          _19 = _1;
           StorageLive(_20);
-          _20 = _2;
--         _18 = unchecked_shl::(move _19, move _20) -> [return: bb6, unwind unreachable];
-+         _18 = ShlUnchecked(move _19, move _20);
+          _20 = _1;
+          StorageLive(_21);
+          _21 = _2;
+-         _19 = unchecked_shl::(move _20, move _21) -> [return: bb6, unwind unreachable];
++         _19 = ShlUnchecked(move _20, move _21);
 +         goto -> bb6;
       }
   
       bb6: {
+          StorageDead(_21);
           StorageDead(_20);
-          StorageDead(_19);
-          StorageLive(_21);
           StorageLive(_22);
-          _22 = _1;
           StorageLive(_23);
-          _23 = _2;
--         _21 = unchecked_shr::(move _22, move _23) -> [return: bb7, unwind unreachable];
-+         _21 = ShrUnchecked(move _22, move _23);
+          _23 = _1;
+          StorageLive(_24);
+          _24 = _2;
+-         _22 = unchecked_shr::(move _23, move _24) -> [return: bb7, unwind unreachable];
++         _22 = ShrUnchecked(move _23, move _24);
 +         goto -> bb7;
       }
   
       bb7: {
+          StorageDead(_24);
           StorageDead(_23);
-          StorageDead(_22);
+          StorageLive(_25);
+          StorageLive(_26);
+          _26 = _1;
+          StorageLive(_27);
+          _27 = _3;
+-         _25 = unchecked_shl::(move _26, move _27) -> [return: bb8, unwind unreachable];
++         _25 = ShlUnchecked(move _26, move _27);
++         goto -> bb8;
+      }
+  
+      bb8: {
+          StorageDead(_27);
+          StorageDead(_26);
+          StorageLive(_28);
+          StorageLive(_29);
+          _29 = _1;
+          StorageLive(_30);
+          _30 = _3;
+-         _28 = unchecked_shr::(move _29, move _30) -> [return: bb9, unwind unreachable];
++         _28 = ShrUnchecked(move _29, move _30);
++         goto -> bb9;
+      }
+  
+      bb9: {
+          StorageDead(_30);
+          StorageDead(_29);
           _0 = const ();
-          StorageDead(_21);
-          StorageDead(_18);
-          StorageDead(_15);
-          StorageDead(_12);
-          StorageDead(_9);
-          StorageDead(_6);
-          StorageDead(_3);
+          StorageDead(_28);
+          StorageDead(_25);
+          StorageDead(_22);
+          StorageDead(_19);
+          StorageDead(_16);
+          StorageDead(_13);
+          StorageDead(_10);
+          StorageDead(_7);
+          StorageDead(_4);
           return;
       }
   }
diff --git a/tests/ui/consts/const-int-unchecked.rs b/tests/ui/consts/const-int-unchecked.rs
index 3fe96c2de23e..8de28aa2bb17 100644
--- a/tests/ui/consts/const-int-unchecked.rs
+++ b/tests/ui/consts/const-int-unchecked.rs
@@ -27,7 +27,7 @@ const SHL_U128: u128 = unsafe { intrinsics::unchecked_shl(5_u128, 128) };
 
 const SHL_I8: i8 = unsafe { intrinsics::unchecked_shl(5_i8, 8) };
 //~^ ERROR evaluation of constant value failed
-const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_16, 16) };
+const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_i16, 16) };
 //~^ ERROR evaluation of constant value failed
 const SHL_I32: i32 = unsafe { intrinsics::unchecked_shl(5_i32, 32) };
 //~^ ERROR evaluation of constant value failed
@@ -40,7 +40,7 @@ const SHL_I128: i128 = unsafe { intrinsics::unchecked_shl(5_i128, 128) };
 
 const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) };
 //~^ ERROR evaluation of constant value failed
-const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) };
+const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -1) };
 //~^ ERROR evaluation of constant value failed
 const SHL_I32_NEG: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -1) };
 //~^ ERROR evaluation of constant value failed
@@ -54,7 +54,7 @@ const SHL_I128_NEG: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -1) };
 
 const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6) };
 //~^ ERROR evaluation of constant value failed
-const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) };
+const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -13) };
 //~^ ERROR evaluation of constant value failed
 const SHL_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -25) };
 //~^ ERROR evaluation of constant value failed
@@ -82,7 +82,7 @@ const SHR_U128: u128 = unsafe { intrinsics::unchecked_shr(5_u128, 128) };
 
 const SHR_I8: i8 = unsafe { intrinsics::unchecked_shr(5_i8, 8) };
 //~^ ERROR evaluation of constant value failed
-const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_16, 16) };
+const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_i16, 16) };
 //~^ ERROR evaluation of constant value failed
 const SHR_I32: i32 = unsafe { intrinsics::unchecked_shr(5_i32, 32) };
 //~^ ERROR evaluation of constant value failed
@@ -95,7 +95,7 @@ const SHR_I128: i128 = unsafe { intrinsics::unchecked_shr(5_i128, 128) };
 
 const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) };
 //~^ ERROR evaluation of constant value failed
-const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) };
+const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -1) };
 //~^ ERROR evaluation of constant value failed
 const SHR_I32_NEG: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -1) };
 //~^ ERROR evaluation of constant value failed
@@ -109,7 +109,7 @@ const SHR_I128_NEG: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -1) };
 
 const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6) };
 //~^ ERROR evaluation of constant value failed
-const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) };
+const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -13) };
 //~^ ERROR evaluation of constant value failed
 const SHR_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -25) };
 //~^ ERROR evaluation of constant value failed
diff --git a/tests/ui/consts/const-int-unchecked.stderr b/tests/ui/consts/const-int-unchecked.stderr
index ad14c8f68f8c..84b222972a13 100644
--- a/tests/ui/consts/const-int-unchecked.stderr
+++ b/tests/ui/consts/const-int-unchecked.stderr
@@ -37,8 +37,8 @@ LL | const SHL_I8: i8 = unsafe { intrinsics::unchecked_shl(5_i8, 8) };
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:30:31
    |
-LL | const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_16, 16) };
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shl`
+LL | const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_i16, 16) };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shl`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:32:31
@@ -67,8 +67,8 @@ LL | const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) };
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:43:35
    |
-LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) };
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl`
+LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -1) };
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:45:35
@@ -97,8 +97,8 @@ LL | const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6)
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:57:42
    |
-LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) };
-   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shl`
+LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -13) };
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shl`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:59:42
@@ -157,8 +157,8 @@ LL | const SHR_I8: i8 = unsafe { intrinsics::unchecked_shr(5_i8, 8) };
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:85:31
    |
-LL | const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_16, 16) };
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shr`
+LL | const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_i16, 16) };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shr`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:87:31
@@ -187,8 +187,8 @@ LL | const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) };
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:98:35
    |
-LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) };
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr`
+LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -1) };
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:100:35
@@ -217,8 +217,8 @@ LL | const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6)
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:112:42
    |
-LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) };
-   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shr`
+LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -13) };
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shr`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:114:42

From 71ea506d3df77b77a38b6f1ad0c7926c08b37f32 Mon Sep 17 00:00:00 2001
From: klensy 
Date: Sat, 30 Mar 2024 17:39:43 +0300
Subject: [PATCH 120/192] bump tracing-tree to 0.3

Only change is https://github.com/davidbarsky/tracing-tree/pull/76
dedupes tracing-log
dupes nu-ansi-term
---
 Cargo.lock                                 | 32 ++++++++++------------
 compiler/rustc_log/Cargo.toml              |  2 +-
 compiler/rustc_pattern_analysis/Cargo.toml |  2 +-
 src/librustdoc/Cargo.toml                  |  2 +-
 4 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index c4501d6e574f..6168a0a17471 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2590,6 +2590,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "nu-ansi-term"
+version = "0.49.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "num-conv"
 version = "0.1.0"
@@ -5778,17 +5787,6 @@ dependencies = [
  "tracing-subscriber",
 ]
 
-[[package]]
-name = "tracing-log"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2"
-dependencies = [
- "log",
- "once_cell",
- "tracing-core",
-]
-
 [[package]]
 name = "tracing-log"
 version = "0.2.0"
@@ -5807,7 +5805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
 dependencies = [
  "matchers",
- "nu-ansi-term",
+ "nu-ansi-term 0.46.0",
  "once_cell",
  "parking_lot",
  "regex",
@@ -5816,18 +5814,18 @@ dependencies = [
  "thread_local",
  "tracing",
  "tracing-core",
- "tracing-log 0.2.0",
+ "tracing-log",
 ]
 
 [[package]]
 name = "tracing-tree"
-version = "0.2.5"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ec6adcab41b1391b08a308cc6302b79f8095d1673f6947c2dc65ffb028b0b2d"
+checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675"
 dependencies = [
- "nu-ansi-term",
+ "nu-ansi-term 0.49.0",
  "tracing-core",
- "tracing-log 0.1.4",
+ "tracing-log",
  "tracing-subscriber",
 ]
 
diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml
index 6009a43e9855..3ff86f700a53 100644
--- a/compiler/rustc_log/Cargo.toml
+++ b/compiler/rustc_log/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
 tracing = "0.1.28"
 tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635
 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
-tracing-tree = "0.2.0"
+tracing-tree = "0.3.0"
 # tidy-alphabetical-end
 
 [dev-dependencies]
diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml
index 6357d18b9da8..0cb47e03441b 100644
--- a/compiler/rustc_pattern_analysis/Cargo.toml
+++ b/compiler/rustc_pattern_analysis/Cargo.toml
@@ -24,7 +24,7 @@ tracing = "0.1"
 
 [dev-dependencies]
 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "ansi"] }
-tracing-tree = "0.2.0"
+tracing-tree = "0.3.0"
 
 [features]
 default = ["rustc"]
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index bd0fbef998b2..d43c0cfa9612 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -20,7 +20,7 @@ serde = { version = "1.0", features = ["derive"] }
 smallvec = "1.8.1"
 tempfile = "3"
 tracing = "0.1"
-tracing-tree = "0.2.0"
+tracing-tree = "0.3.0"
 threadpool = "1.8.1"
 
 [dependencies.tracing-subscriber]

From bda301ead8ab19814853dd876dbf93c09e7a4282 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 29 Mar 2024 19:50:08 -0400
Subject: [PATCH 121/192] Stop calling visitors V

---
 .../src/diagnostics/mutability_errors.rs      | 15 ++++++++++---
 .../src/collect/resolve_bound_vars.rs         |  8 ++++---
 .../rustc_hir_typeck/src/fn_ctxt/checks.rs    | 21 +++++++++----------
 .../error_reporting/type_err_ctxt_ext.rs      |  9 ++++----
 4 files changed, 32 insertions(+), 21 deletions(-)

diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 26bb68003480..bd068b29c123 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -540,19 +540,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
+    /// Suggest `map[k] = v` => `map.insert(k, v)` and the like.
     fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'tcx>, span: Span) {
         let Some(adt) = ty.ty_adt_def() else { return };
         let did = adt.did();
         if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
             || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
         {
-            struct V<'a, 'tcx> {
+            /// Walks through the HIR, looking for the corresponding span for this error.
+            /// When it finds it, see if it corresponds to assignment operator whose LHS
+            /// is an index expr.
+            struct SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> {
                 assign_span: Span,
                 err: &'a mut Diag<'tcx>,
                 ty: Ty<'tcx>,
                 suggested: bool,
             }
-            impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+            impl<'a, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> {
                 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
                     hir::intravisit::walk_stmt(self, stmt);
                     let expr = match stmt.kind {
@@ -645,7 +649,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) else { return };
             let body = self.infcx.tcx.hir().body(body_id);
 
-            let mut v = V { assign_span: span, err, ty, suggested: false };
+            let mut v = SuggestIndexOperatorAlternativeVisitor {
+                assign_span: span,
+                err,
+                ty,
+                suggested: false,
+            };
             v.visit_body(body);
             if !v.suggested {
                 err.help(format!(
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index ee3436805ca7..0b8ac9926e41 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -418,8 +418,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
         {
             if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
                 fn span_of_infer(ty: &hir::Ty<'_>) -> Option {
-                    struct V;
-                    impl<'v> Visitor<'v> for V {
+                    /// Look for `_` anywhere in the signature of a `for<> ||` closure.
+                    /// This is currently disallowed.
+                    struct FindInferInClosureWithBinder;
+                    impl<'v> Visitor<'v> for FindInferInClosureWithBinder {
                         type Result = ControlFlow;
                         fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result {
                             if matches!(t.kind, hir::TyKind::Infer) {
@@ -429,7 +431,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                             }
                         }
                     }
-                    V.visit_ty(ty).break_value()
+                    FindInferInClosureWithBinder.visit_ty(ty).break_value()
                 }
 
                 let infer_in_rt_sp = match fn_decl.output {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 8ea1a88be5de..5f5ff40fb9f4 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1916,22 +1916,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &'tcx hir::Pat<'tcx>,
         ty: Ty<'tcx>,
     ) {
-        struct V {
-            pat_hir_ids: Vec,
-        }
-
-        impl<'tcx> Visitor<'tcx> for V {
-            fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
-                self.pat_hir_ids.push(p.hir_id);
-                hir::intravisit::walk_pat(self, p);
-            }
-        }
         if let Err(guar) = ty.error_reported() {
+            struct OverwritePatternsWithError {
+                pat_hir_ids: Vec,
+            }
+            impl<'tcx> Visitor<'tcx> for OverwritePatternsWithError {
+                fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
+                    self.pat_hir_ids.push(p.hir_id);
+                    hir::intravisit::walk_pat(self, p);
+                }
+            }
             // Override the types everywhere with `err()` to avoid knock on errors.
             let err = Ty::new_error(self.tcx, guar);
             self.write_ty(hir_id, err);
             self.write_ty(pat.hir_id, err);
-            let mut visitor = V { pat_hir_ids: vec![] };
+            let mut visitor = OverwritePatternsWithError { pat_hir_ids: vec![] };
             hir::intravisit::walk_pat(&mut visitor, pat);
             // Mark all the subpatterns as `{type error}` as well. This allows errors for specific
             // subpatterns to be silenced.
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 7a62030353d9..aef98dbad5fe 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -1128,10 +1128,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         err: &mut Diag<'_>,
     ) -> bool {
         let span = obligation.cause.span;
-        struct V {
+        /// Look for the (direct) sub-expr of `?`, and return it if it's a `.` method call.
+        struct FindMethodSubexprOfTry {
             search_span: Span,
         }
-        impl<'v> Visitor<'v> for V {
+        impl<'v> Visitor<'v> for FindMethodSubexprOfTry {
             type Result = ControlFlow<&'v hir::Expr<'v>>;
             fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
                 if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind
@@ -1149,8 +1150,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id,
             _ => return false,
         };
-        let ControlFlow::Break(expr) =
-            (V { search_span: span }).visit_body(self.tcx.hir().body(*body_id))
+        let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span })
+            .visit_body(self.tcx.hir().body(*body_id))
         else {
             return false;
         };

From e1b8441899b87e52d0b368f55fc921ec668cde1f Mon Sep 17 00:00:00 2001
From: Nadrieril 
Date: Sat, 30 Mar 2024 16:18:18 +0100
Subject: [PATCH 122/192] Require enum indices to be contiguous

---
 .../rustc_pattern_analysis/src/constructor.rs | 11 ++---
 compiler/rustc_pattern_analysis/src/lib.rs    | 49 ++-----------------
 2 files changed, 8 insertions(+), 52 deletions(-)

diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index 95c5556410d3..1c9a9ab0f720 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -155,13 +155,13 @@ use std::iter::once;
 use smallvec::SmallVec;
 
 use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
-use rustc_index::bit_set::GrowableBitSet;
+use rustc_index::bit_set::{BitSet, GrowableBitSet};
+use rustc_index::IndexVec;
 
 use self::Constructor::*;
 use self::MaybeInfiniteInt::*;
 use self::SliceKind::*;
 
-use crate::index;
 use crate::PatCx;
 
 /// Whether we have seen a constructor in the column or not.
@@ -920,10 +920,7 @@ pub enum ConstructorSet {
     Struct { empty: bool },
     /// This type has the following list of constructors. If `variants` is empty and
     /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead.
-    Variants {
-        variants: index::IdxContainer,
-        non_exhaustive: bool,
-    },
+    Variants { variants: IndexVec, non_exhaustive: bool },
     /// The type is `&T`.
     Ref,
     /// The type is a union.
@@ -1025,7 +1022,7 @@ impl ConstructorSet {
                 }
             }
             ConstructorSet::Variants { variants, non_exhaustive } => {
-                let mut seen_set = index::IdxSet::new_empty(variants.len());
+                let mut seen_set = BitSet::new_empty(variants.len());
                 for idx in seen.iter().filter_map(|c| c.as_variant()) {
                     seen_set.insert(idx);
                 }
diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs
index 1a1da5c55f60..6e8843d90497 100644
--- a/compiler/rustc_pattern_analysis/src/lib.rs
+++ b/compiler/rustc_pattern_analysis/src/lib.rs
@@ -25,50 +25,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
 use std::fmt;
 
-#[cfg(feature = "rustc")]
-pub mod index {
-    // Faster version when the indices of variants are `0..variants.len()`.
-    pub use rustc_index::bit_set::BitSet as IdxSet;
-    pub use rustc_index::Idx;
-    pub use rustc_index::IndexVec as IdxContainer;
-}
-#[cfg(not(feature = "rustc"))]
-pub mod index {
-    // Slower version when the indices of variants are something else.
-    pub trait Idx: Copy + PartialEq + Eq + std::hash::Hash {}
-    impl Idx for T {}
-
-    #[derive(Debug)]
-    pub struct IdxContainer(pub rustc_hash::FxHashMap);
-    impl IdxContainer {
-        pub fn len(&self) -> usize {
-            self.0.len()
-        }
-        pub fn iter_enumerated(&self) -> impl Iterator {
-            self.0.iter().map(|(k, v)| (*k, v))
-        }
-    }
-
-    impl FromIterator for IdxContainer {
-        fn from_iter>(iter: T) -> Self {
-            Self(iter.into_iter().enumerate().collect())
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct IdxSet(pub rustc_hash::FxHashSet);
-    impl IdxSet {
-        pub fn new_empty(_len: usize) -> Self {
-            Self(Default::default())
-        }
-        pub fn contains(&self, elem: T) -> bool {
-            self.0.contains(&elem)
-        }
-        pub fn insert(&mut self, elem: T) {
-            self.0.insert(elem);
-        }
-    }
-}
+// Re-exports to avoid rustc_index version issues.
+pub use rustc_index::Idx;
+pub use rustc_index::IndexVec;
 
 #[cfg(feature = "rustc")]
 use rustc_middle::ty::Ty;
@@ -96,7 +55,7 @@ pub trait PatCx: Sized + fmt::Debug {
     /// Errors that can abort analysis.
     type Error: fmt::Debug;
     /// The index of an enum variant.
-    type VariantIdx: Clone + index::Idx + fmt::Debug;
+    type VariantIdx: Clone + Idx + fmt::Debug;
     /// A string literal
     type StrLit: Clone + PartialEq + fmt::Debug;
     /// Extra data to store in a match arm.

From c749483e2670162a0d185376cd2d97526eeb6695 Mon Sep 17 00:00:00 2001
From: David Carlier 
Date: Sat, 30 Mar 2024 14:19:05 +0000
Subject: [PATCH 123/192] std::thread: adding freebsd/netbsd to the linux's
 get_name implementation.

---
 library/std/src/sys/pal/unix/thread.rs | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 4cd7c0e3059b..01e3c3cbc6fd 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -225,9 +225,14 @@ impl Thread {
         // Newlib, Emscripten, and VxWorks have no way to set a thread name.
     }
 
-    #[cfg(target_os = "linux")]
+    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd",))]
     pub fn get_name() -> Option {
+        #[cfg(target_os = "linux")]
         const TASK_COMM_LEN: usize = 16;
+        #[cfg(target_os = "freebsd")]
+        const TASK_COMM_LEN: usize = libc::MAXCOMLEN + 1;
+        #[cfg(target_os = "netbsd")]
+        const TASK_COMM_LEN: usize = 32;
         let mut name = vec![0u8; TASK_COMM_LEN];
         let res = unsafe {
             libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len())
@@ -254,6 +259,8 @@ impl Thread {
 
     #[cfg(not(any(
         target_os = "linux",
+        target_os = "freebsd",
+        target_os = "netbsd",
         target_os = "macos",
         target_os = "ios",
         target_os = "tvos",

From 2a939422ca7471317b7b0d617272e6c18ca5a291 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Thu, 28 Mar 2024 23:03:31 +0100
Subject: [PATCH 124/192] prepare_tool_cargo: add support for a miri-test mode,
 and use it in the cargo-miri smoke test and Miri sysroot build

---
 src/bootstrap/src/core/build_steps/run.rs  |  14 +--
 src/bootstrap/src/core/build_steps/test.rs | 100 +++++++++------------
 src/bootstrap/src/core/builder.rs          |  46 ++++++++--
 src/bootstrap/src/lib.rs                   |   2 +
 4 files changed, 85 insertions(+), 77 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index 61ee2fc1f6f3..bad51825235e 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -121,7 +121,6 @@ impl Step for ReplaceVersionPlaceholder {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Miri {
-    stage: u32,
     host: TargetSelection,
     target: TargetSelection,
 }
@@ -135,22 +134,17 @@ impl Step for Miri {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Miri {
-            stage: run.builder.top_stage,
-            host: run.build_triple(),
-            target: run.target,
-        });
+        run.builder.ensure(Miri { host: run.build_triple(), target: run.target });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let stage = self.stage;
+        let stage = builder.top_stage;
         let host = self.host;
         let target = self.target;
         let compiler = builder.compiler(stage, host);
 
-        let miri =
-            builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() });
-        let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler, &miri, target);
+        let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host);
+        let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler_std, target);
 
         // # Run miri.
         // Running it via `cargo run` as that figures out the right dylib path.
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 11baf0cda80b..2561f58e3562 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -493,7 +493,6 @@ impl Step for RustDemangler {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Miri {
-    stage: u32,
     host: TargetSelection,
     target: TargetSelection,
 }
@@ -502,41 +501,31 @@ impl Miri {
     /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put.
     pub fn build_miri_sysroot(
         builder: &Builder<'_>,
-        compiler: Compiler,
-        miri: &Path,
+        compiler_std: Compiler,
         target: TargetSelection,
     ) -> String {
-        let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot");
-        let mut cargo = tool::prepare_tool_cargo(
+        let miri_sysroot = builder.out.join(compiler_std.host.triple).join("miri-sysroot");
+        let mut cargo = builder::Cargo::new(
             builder,
-            compiler,
-            Mode::ToolRustc,
-            compiler.host,
-            "run",
-            "src/tools/miri/cargo-miri",
-            SourceType::InTree,
-            &[],
+            compiler_std, // this is compiler+1; cargo_miri_cmd will do -1 again
+            Mode::Std,
+            SourceType::Submodule,
+            target,
+            "miri-setup",
         );
-        cargo.add_rustc_lib_path(builder);
-        cargo.arg("--").arg("miri").arg("setup");
-        cargo.arg("--target").arg(target.rustc_target_arg());
 
         // Tell `cargo miri setup` where to find the sources.
         cargo.env("MIRI_LIB_SRC", builder.src.join("library"));
-        // Tell it where to find Miri.
-        cargo.env("MIRI", miri);
         // Tell it where to put the sysroot.
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
-        // Debug things.
-        cargo.env("RUST_BACKTRACE", "1");
 
         let mut cargo = Command::from(cargo);
         let _guard = builder.msg(
             Kind::Build,
-            compiler.stage + 1,
+            compiler_std.stage,
             "miri sysroot",
-            compiler.host,
-            compiler.host,
+            compiler_std.host,
+            compiler_std.host,
         );
         builder.run(&mut cargo);
 
@@ -574,16 +563,12 @@ impl Step for Miri {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Miri {
-            stage: run.builder.top_stage,
-            host: run.build_triple(),
-            target: run.target,
-        });
+        run.builder.ensure(Miri { host: run.build_triple(), target: run.target });
     }
 
     /// Runs `cargo test` for miri.
     fn run(self, builder: &Builder<'_>) {
-        let stage = self.stage;
+        let stage = builder.top_stage;
         let host = self.host;
         let target = self.target;
         let compiler = builder.compiler(stage, host);
@@ -592,18 +577,15 @@ impl Step for Miri {
         let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host);
 
         let miri =
-            builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() });
-        let _cargo_miri = builder.ensure(tool::CargoMiri {
-            compiler,
-            target: self.host,
-            extra_features: Vec::new(),
-        });
+            builder.ensure(tool::Miri { compiler, target: host, extra_features: Vec::new() });
+        // the ui tests also assume cargo-miri has been built
+        builder.ensure(tool::CargoMiri { compiler, target: host, extra_features: Vec::new() });
         // The stdlib we need might be at a different stage. And just asking for the
         // sysroot does not seem to populate it, so we do that first.
         builder.ensure(compile::Std::new(compiler_std, host));
         let sysroot = builder.sysroot(compiler_std);
         // We also need a Miri sysroot.
-        let miri_sysroot = Miri::build_miri_sysroot(builder, compiler, &miri, target);
+        let miri_sysroot = Miri::build_miri_sysroot(builder, compiler_std, target);
 
         // # Run `cargo test`.
         let mut cargo = tool::prepare_tool_cargo(
@@ -616,10 +598,13 @@ impl Step for Miri {
             SourceType::InTree,
             &[],
         );
-        let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target);
 
         cargo.add_rustc_lib_path(builder);
 
+        // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test
+        // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`.
+        let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder);
+
         // miri tests need to know about the stage sysroot
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
         cargo.env("MIRI_HOST_SYSROOT", &sysroot);
@@ -632,10 +617,8 @@ impl Step for Miri {
         // Set the target.
         cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg());
 
-        // This can NOT be `run_cargo_test` since the Miri test runner
-        // does not understand the flags added by `add_flags_and_try_run_test`.
-        let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder);
         {
+            let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target);
             let _time = helpers::timeit(builder);
             builder.run(&mut cargo);
         }
@@ -650,8 +633,14 @@ impl Step for Miri {
             // Optimizations can change error locations and remove UB so don't run `fail` tests.
             cargo.args(["tests/pass", "tests/panic"]);
 
-            let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder);
             {
+                let _guard = builder.msg_sysroot_tool(
+                    Kind::Test,
+                    compiler.stage,
+                    "miri (mir-opt-level 4)",
+                    host,
+                    target,
+                );
                 let _time = helpers::timeit(builder);
                 builder.run(&mut cargo);
             }
@@ -660,28 +649,20 @@ impl Step for Miri {
         // # Run `cargo miri test`.
         // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures
         // that we get the desired output), but that is sufficient to make sure that the libtest harness
-        // itself executes properly under Miri.
+        // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode.
+        // Everything here needs `compiler_std` to be actually testing the Miri in the current stage.
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            compiler,
-            Mode::ToolRustc,
-            host,
-            "run",
-            "src/tools/miri/cargo-miri",
+            compiler_std,  // this is compiler+1; cargo_miri_cmd will do -1 again
+            Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test!
+            target,
+            "miri-test",
+            "src/tools/miri/test-cargo-miri",
             SourceType::Submodule,
             &[],
         );
-        cargo.add_rustc_lib_path(builder);
-        cargo.arg("--").arg("miri").arg("test");
-        if builder.config.locked_deps {
-            cargo.arg("--locked");
-        }
-        cargo
-            .arg("--manifest-path")
-            .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml"));
-        cargo.arg("--target").arg(target.rustc_target_arg());
 
-        // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "run", not a "test".
+        // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "miri", not a "test".
         // Also, we want the rustdoc from the "next" stage for the same reason that we build a std from the next stage.
         // So let's just set that here, and bypass bootstrap's RUSTDOC (just like cargo-miri already ignores bootstrap's RUSTC_WRAPPER).
         if builder.doc_tests != DocTests::No {
@@ -697,17 +678,16 @@ impl Step for Miri {
             }
         }
 
-        // Tell `cargo miri` where to find things.
+        // Tell `cargo miri` where to find the sysroots.
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
         cargo.env("MIRI_HOST_SYSROOT", sysroot);
-        cargo.env("MIRI", &miri);
-        // Debug things.
-        cargo.env("RUST_BACKTRACE", "1");
 
         // Finally, pass test-args and run everything.
         cargo.arg("--").args(builder.config.test_args());
         let mut cargo = Command::from(cargo);
         {
+            let _guard =
+                builder.msg_sysroot_tool(Kind::Test, compiler.stage, "cargo-miri", host, target);
             let _time = helpers::timeit(builder);
             builder.run(&mut cargo);
         }
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 7f93fdc72ef0..8294abcb4eb7 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1253,6 +1253,30 @@ impl<'a> Builder<'a> {
         cmd
     }
 
+    pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> Command {
+        assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0");
+        let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build);
+
+        let miri = self.ensure(tool::Miri {
+            compiler: build_compiler,
+            target: self.build.build,
+            extra_features: Vec::new(),
+        });
+        let cargo_miri = self.ensure(tool::CargoMiri {
+            compiler: build_compiler,
+            target: self.build.build,
+            extra_features: Vec::new(),
+        });
+        // Invoke cargo-miri, make sure we can find miri and cargo.
+        let mut cmd = Command::new(cargo_miri);
+        cmd.env("MIRI", &miri);
+        cmd.env("CARGO", &self.initial_cargo);
+        // Need to add the run_compiler libs. Not entirely sure why that has to be one stage up from
+        // what Miri was built for.
+        self.add_rustc_lib_path(run_compiler, &mut cmd);
+        cmd
+    }
+
     pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command {
         let mut cmd = Command::new(self.bootstrap_out.join("rustdoc"));
         cmd.env("RUSTC_STAGE", compiler.stage.to_string())
@@ -1296,18 +1320,24 @@ impl<'a> Builder<'a> {
         target: TargetSelection,
         cmd: &str,
     ) -> Command {
-        let mut cargo = if cmd == "clippy" {
-            self.cargo_clippy_cmd(compiler)
+        let mut cargo;
+        if cmd == "clippy" {
+            cargo = self.cargo_clippy_cmd(compiler);
+            cargo.arg(cmd);
+        } else if let Some(subcmd) = cmd.strip_prefix("miri-") {
+            cargo = self.cargo_miri_cmd(compiler);
+            cargo.arg("miri").arg(subcmd);
         } else {
-            Command::new(&self.initial_cargo)
-        };
+            cargo = Command::new(&self.initial_cargo);
+            cargo.arg(cmd);
+        }
 
         // Run cargo from the source root so it can find .cargo/config.
         // This matters when using vendoring and the working directory is outside the repository.
         cargo.current_dir(&self.src);
 
         let out_dir = self.stage_out(compiler, mode);
-        cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd);
+        cargo.env("CARGO_TARGET_DIR", &out_dir);
 
         // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger`
         // from out of tree it shouldn't matter, since x.py is only used for
@@ -1337,7 +1367,8 @@ impl<'a> Builder<'a> {
 
         if self.config.rust_optimize.is_release() {
             // FIXME: cargo bench/install do not accept `--release`
-            if cmd != "bench" && cmd != "install" {
+            // and miri doesn't want it
+            if cmd != "bench" && cmd != "install" && !cmd.starts_with("miri-") {
                 cargo.arg("--release");
             }
         }
@@ -1353,7 +1384,8 @@ impl<'a> Builder<'a> {
     /// Cargo. This cargo will be configured to use `compiler` as the actual
     /// rustc compiler, its output will be scoped by `mode`'s output directory,
     /// it will pass the `--target` flag for the specified `target`, and will be
-    /// executing the Cargo command `cmd`.
+    /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands
+    /// to be run with Miri.
     fn cargo(
         &self,
         compiler: Compiler,
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index d8397ab51de3..44452446eb87 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -250,6 +250,8 @@ pub enum Mode {
     /// directory. This is for miscellaneous sets of tools that are built
     /// using the bootstrap stage0 compiler in its entirety (target libraries
     /// and all). Typically these tools compile with stable Rust.
+    ///
+    /// Only works for stage 0.
     ToolBootstrap,
 
     /// Build a tool which uses the locally built std, placing output in the

From e477488267e1c02d18c8bca873d560c7d81d85e5 Mon Sep 17 00:00:00 2001
From: Oneirical 
Date: Thu, 28 Mar 2024 22:49:43 -0400
Subject: [PATCH 125/192] Rewrite core-no-fp-fmt-parse in Rust

Rewrite core-no-fp-fmt-parse in Rust

fix: missing import

fix: tidiness check

more tidy checks

remove tidy line length ignore

new helper functions + arg_path generic

fix: remove unused import

delete arg_path, change arg_path to input
---
 src/tools/run-make-support/src/rustc.rs         | 14 ++++++++++++++
 .../tidy/src/allowed_run_make_makefiles.txt     |  1 -
 tests/run-make/core-no-fp-fmt-parse/Makefile    |  4 ----
 tests/run-make/core-no-fp-fmt-parse/rmake.rs    | 17 +++++++++++++++++
 4 files changed, 31 insertions(+), 5 deletions(-)
 delete mode 100644 tests/run-make/core-no-fp-fmt-parse/Makefile
 create mode 100644 tests/run-make/core-no-fp-fmt-parse/rmake.rs

diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs
index d0ab8df42d28..1b358817a795 100644
--- a/src/tools/run-make-support/src/rustc.rs
+++ b/src/tools/run-make-support/src/rustc.rs
@@ -104,6 +104,20 @@ impl Rustc {
         self
     }
 
+    /// Specify the crate type.
+    pub fn crate_type(&mut self, crate_type: &str) -> &mut Self {
+        self.cmd.arg("--crate-type");
+        self.cmd.arg(crate_type);
+        self
+    }
+
+    /// Specify the edition year.
+    pub fn edition(&mut self, edition: &str) -> &mut Self {
+        self.cmd.arg("--edition");
+        self.cmd.arg(edition);
+        self
+    }
+
     /// Generic command arguments provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`.
     /// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z ` or `-C `
     /// is passed (note the space).
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index bdee3afa6b71..40950e6ba443 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -31,7 +31,6 @@ run-make/compiler-rt-works-on-mingw/Makefile
 run-make/compressed-debuginfo/Makefile
 run-make/const-prop-lint/Makefile
 run-make/const_fn_mir/Makefile
-run-make/core-no-fp-fmt-parse/Makefile
 run-make/core-no-oom-handling/Makefile
 run-make/crate-data-smoke/Makefile
 run-make/crate-hash-rustc-version/Makefile
diff --git a/tests/run-make/core-no-fp-fmt-parse/Makefile b/tests/run-make/core-no-fp-fmt-parse/Makefile
deleted file mode 100644
index 837664d92b93..000000000000
--- a/tests/run-make/core-no-fp-fmt-parse/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-include ../tools.mk
-
-all:
-	$(RUSTC) --edition=2021 -Dwarnings --crate-type=rlib ../../../library/core/src/lib.rs --cfg no_fp_fmt_parse
diff --git a/tests/run-make/core-no-fp-fmt-parse/rmake.rs b/tests/run-make/core-no-fp-fmt-parse/rmake.rs
new file mode 100644
index 000000000000..2748d4359c36
--- /dev/null
+++ b/tests/run-make/core-no-fp-fmt-parse/rmake.rs
@@ -0,0 +1,17 @@
+// This test checks that the core library of Rust can be compiled without enabling
+// support for formatting and parsing floating-point numbers.
+
+extern crate run_make_support;
+
+use run_make_support::rustc;
+use std::path::PathBuf;
+
+fn main() {
+    rustc()
+        .edition("2021")
+        .arg("-Dwarnings")
+        .crate_type("rlib")
+        .input("../../../library/core/src/lib.rs")
+        .cfg("no_fp_fmt_parse")
+        .run();
+}

From 5b9d7ab558fe6ee53bd40e16a20c25716f3c9e56 Mon Sep 17 00:00:00 2001
From: joboet 
Date: Sun, 31 Mar 2024 11:24:33 +0200
Subject: [PATCH 126/192] std: move UNIX stack overflow guard page handling
 into `stack_overflow.rs`

---
 .../std/src/sys/pal/unix/stack_overflow.rs    | 312 ++++++++++++++++--
 library/std/src/sys/pal/unix/thread.rs        | 285 ----------------
 2 files changed, 287 insertions(+), 310 deletions(-)

diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs
index 78a599077c75..26c49257ad00 100644
--- a/library/std/src/sys/pal/unix/stack_overflow.rs
+++ b/library/std/src/sys/pal/unix/stack_overflow.rs
@@ -11,7 +11,7 @@ pub struct Handler {
 
 impl Handler {
     pub unsafe fn new() -> Handler {
-        make_handler()
+        make_handler(false)
     }
 
     fn null() -> Handler {
@@ -29,34 +29,41 @@ impl Drop for Handler {
 
 #[cfg(any(
     target_os = "linux",
-    target_os = "macos",
-    target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "hurd",
-    target_os = "solaris",
-    target_os = "illumos",
+    target_os = "macos",
     target_os = "netbsd",
-    target_os = "openbsd"
+    target_os = "openbsd",
+    target_os = "solaris"
 ))]
 mod imp {
     use super::Handler;
+    use crate::cell::Cell;
     use crate::io;
     use crate::mem;
+    use crate::ops::Range;
     use crate::ptr;
+    use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
+    use crate::sys::pal::unix::os;
     use crate::thread;
 
-    use libc::MAP_FAILED;
     #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
-    use libc::{mmap as mmap64, munmap};
+    use libc::{mmap as mmap64, mprotect, munmap};
     #[cfg(all(target_os = "linux", target_env = "gnu"))]
-    use libc::{mmap64, munmap};
-    use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL};
+    use libc::{mmap64, mprotect, munmap};
+    use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV, SIG_DFL};
     use libc::{sigaltstack, SS_DISABLE};
-    use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV};
+    use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
 
-    use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
-    use crate::sys::pal::unix::os::page_size;
-    use crate::sys_common::thread_info;
+    // We use a TLS variable to store the address of the guard page. While TLS
+    // variables are not guaranteed to be signal-safe, this works out in practice
+    // since we make sure to write to the variable before the signal stack is
+    // installed, thereby ensuring that the variable is always allocated when
+    // the signal handler is called.
+    thread_local! {
+        // FIXME: use `Range` once that implements `Copy`.
+        static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) };
+    }
 
     // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
     // (unmapped pages) at the end of every thread's stack, so if a thread ends
@@ -84,12 +91,12 @@ mod imp {
         info: *mut libc::siginfo_t,
         _data: *mut libc::c_void,
     ) {
-        let guard = thread_info::stack_guard().unwrap_or(0..0);
+        let (start, end) = GUARD.get();
         let addr = (*info).si_addr() as usize;
 
         // If the faulting address is within the guard page, then we print a
         // message saying so and abort.
-        if guard.start <= addr && addr < guard.end {
+        if start <= addr && addr < end {
             rtprintpanic!(
                 "\nthread '{}' has overflowed its stack\n",
                 thread::current().name().unwrap_or("")
@@ -105,10 +112,17 @@ mod imp {
         }
     }
 
+    static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
     static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut());
     static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false);
 
     pub unsafe fn init() {
+        PAGE_SIZE.store(os::page_size(), Ordering::Relaxed);
+
+        // Always write to GUARD to ensure the TLS variable is allocated.
+        let guard = install_main_guard().unwrap_or(0..0);
+        GUARD.set((guard.start, guard.end));
+
         let mut action: sigaction = mem::zeroed();
         for &signal in &[SIGSEGV, SIGBUS] {
             sigaction(signal, ptr::null_mut(), &mut action);
@@ -121,7 +135,7 @@ mod imp {
             }
         }
 
-        let handler = make_handler();
+        let handler = make_handler(true);
         MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
         mem::forget(handler);
     }
@@ -150,7 +164,7 @@ mod imp {
         let flags = MAP_PRIVATE | MAP_ANON;
 
         let sigstack_size = sigstack_size();
-        let page_size = page_size();
+        let page_size = PAGE_SIZE.load(Ordering::Relaxed);
 
         let stackp = mmap64(
             ptr::null_mut(),
@@ -172,10 +186,17 @@ mod imp {
         libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size }
     }
 
-    pub unsafe fn make_handler() -> Handler {
+    pub unsafe fn make_handler(main_thread: bool) -> Handler {
         if !NEED_ALTSTACK.load(Ordering::Relaxed) {
             return Handler::null();
         }
+
+        if !main_thread {
+            // Always write to GUARD to ensure the TLS variable is allocated.
+            let guard = current_guard().unwrap_or(0..0);
+            GUARD.set((guard.start, guard.end));
+        }
+
         let mut stack = mem::zeroed();
         sigaltstack(ptr::null(), &mut stack);
         // Configure alternate signal stack, if one is not already set.
@@ -191,7 +212,7 @@ mod imp {
     pub unsafe fn drop_handler(data: *mut libc::c_void) {
         if !data.is_null() {
             let sigstack_size = sigstack_size();
-            let page_size = page_size();
+            let page_size = PAGE_SIZE.load(Ordering::Relaxed);
             let stack = libc::stack_t {
                 ss_sp: ptr::null_mut(),
                 ss_flags: SS_DISABLE,
@@ -225,25 +246,266 @@ mod imp {
     fn sigstack_size() -> usize {
         libc::SIGSTKSZ
     }
+
+    #[cfg(target_os = "solaris")]
+    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+        let mut current_stack: libc::stack_t = crate::mem::zeroed();
+        assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
+        Some(current_stack.ss_sp)
+    }
+
+    #[cfg(target_os = "macos")]
+    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+        let th = libc::pthread_self();
+        let stackptr = libc::pthread_get_stackaddr_np(th);
+        Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th)))
+    }
+
+    #[cfg(target_os = "openbsd")]
+    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+        let mut current_stack: libc::stack_t = crate::mem::zeroed();
+        assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
+
+        let stack_ptr = current_stack.ss_sp;
+        let stackaddr = if libc::pthread_main_np() == 1 {
+            // main thread
+            stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
+        } else {
+            // new thread
+            stack_ptr.addr() - current_stack.ss_size
+        };
+        Some(stack_ptr.with_addr(stackaddr))
+    }
+
+    #[cfg(any(
+        target_os = "android",
+        target_os = "freebsd",
+        target_os = "netbsd",
+        target_os = "hurd",
+        target_os = "linux",
+        target_os = "l4re"
+    ))]
+    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+        let mut ret = None;
+        let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
+        #[cfg(target_os = "freebsd")]
+        assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+        #[cfg(target_os = "freebsd")]
+        let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
+        #[cfg(not(target_os = "freebsd"))]
+        let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
+        if e == 0 {
+            let mut stackaddr = crate::ptr::null_mut();
+            let mut stacksize = 0;
+            assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0);
+            ret = Some(stackaddr);
+        }
+        if e == 0 || cfg!(target_os = "freebsd") {
+            assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+        }
+        ret
+    }
+
+    unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> {
+        let page_size = PAGE_SIZE.load(Ordering::Relaxed);
+        let stackptr = get_stack_start()?;
+        let stackaddr = stackptr.addr();
+
+        // Ensure stackaddr is page aligned! A parent process might
+        // have reset RLIMIT_STACK to be non-page aligned. The
+        // pthread_attr_getstack() reports the usable stack area
+        // stackaddr < stackaddr + stacksize, so if stackaddr is not
+        // page-aligned, calculate the fix such that stackaddr <
+        // new_page_aligned_stackaddr < stackaddr + stacksize
+        let remainder = stackaddr % page_size;
+        Some(if remainder == 0 {
+            stackptr
+        } else {
+            stackptr.with_addr(stackaddr + page_size - remainder)
+        })
+    }
+
+    unsafe fn install_main_guard() -> Option> {
+        let page_size = PAGE_SIZE.load(Ordering::Relaxed);
+        if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
+            // Linux doesn't allocate the whole stack right away, and
+            // the kernel has its own stack-guard mechanism to fault
+            // when growing too close to an existing mapping. If we map
+            // our own guard, then the kernel starts enforcing a rather
+            // large gap above that, rendering much of the possible
+            // stack space useless. See #43052.
+            //
+            // Instead, we'll just note where we expect rlimit to start
+            // faulting, so our handler can report "stack overflow", and
+            // trust that the kernel's own stack guard will work.
+            let stackptr = get_stack_start_aligned()?;
+            let stackaddr = stackptr.addr();
+            Some(stackaddr - page_size..stackaddr)
+        } else if cfg!(all(target_os = "linux", target_env = "musl")) {
+            // For the main thread, the musl's pthread_attr_getstack
+            // returns the current stack size, rather than maximum size
+            // it can eventually grow to. It cannot be used to determine
+            // the position of kernel's stack guard.
+            None
+        } else if cfg!(target_os = "freebsd") {
+            // FreeBSD's stack autogrows, and optionally includes a guard page
+            // at the bottom. If we try to remap the bottom of the stack
+            // ourselves, FreeBSD's guard page moves upwards. So we'll just use
+            // the builtin guard page.
+            let stackptr = get_stack_start_aligned()?;
+            let guardaddr = stackptr.addr();
+            // Technically the number of guard pages is tunable and controlled
+            // by the security.bsd.stack_guard_page sysctl.
+            // By default it is 1, checking once is enough since it is
+            // a boot time config value.
+            static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new();
+
+            let pages = PAGES.get_or_init(|| {
+                use crate::sys::weak::dlsym;
+                dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int);
+                let mut guard: usize = 0;
+                let mut size = crate::mem::size_of_val(&guard);
+                let oid = crate::ffi::CStr::from_bytes_with_nul(
+                    b"security.bsd.stack_guard_page\0",
+                )
+                .unwrap();
+                match sysctlbyname.get() {
+                    Some(fcn) => {
+                        if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 {
+                            guard
+                        } else {
+                            1
+                        }
+                    },
+                    _ => 1,
+                }
+            });
+            Some(guardaddr..guardaddr + pages * page_size)
+        } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) {
+            // OpenBSD stack already includes a guard page, and stack is
+            // immutable.
+            // NetBSD stack includes the guard page.
+            //
+            // We'll just note where we expect rlimit to start
+            // faulting, so our handler can report "stack overflow", and
+            // trust that the kernel's own stack guard will work.
+            let stackptr = get_stack_start_aligned()?;
+            let stackaddr = stackptr.addr();
+            Some(stackaddr - page_size..stackaddr)
+        } else {
+            // Reallocate the last page of the stack.
+            // This ensures SIGBUS will be raised on
+            // stack overflow.
+            // Systems which enforce strict PAX MPROTECT do not allow
+            // to mprotect() a mapping with less restrictive permissions
+            // than the initial mmap() used, so we mmap() here with
+            // read/write permissions and only then mprotect() it to
+            // no permissions at all. See issue #50313.
+            let stackptr = get_stack_start_aligned()?;
+            let result = mmap64(
+                stackptr,
+                page_size,
+                PROT_READ | PROT_WRITE,
+                MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+                -1,
+                0,
+            );
+            if result != stackptr || result == MAP_FAILED {
+                panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
+            }
+
+            let result = mprotect(stackptr, page_size, PROT_NONE);
+            if result != 0 {
+                panic!("failed to protect the guard page: {}", io::Error::last_os_error());
+            }
+
+            let guardaddr = stackptr.addr();
+
+            Some(guardaddr..guardaddr + page_size)
+        }
+    }
+
+    #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))]
+    unsafe fn current_guard() -> Option> {
+        let stackptr = get_stack_start()?;
+        let stackaddr = stackptr.addr();
+        Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
+    }
+
+    #[cfg(any(
+        target_os = "android",
+        target_os = "freebsd",
+        target_os = "hurd",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_os = "l4re"
+    ))]
+    unsafe fn current_guard() -> Option> {
+        let mut ret = None;
+        let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
+        #[cfg(target_os = "freebsd")]
+        assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+        #[cfg(target_os = "freebsd")]
+        let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
+        #[cfg(not(target_os = "freebsd"))]
+        let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
+        if e == 0 {
+            let mut guardsize = 0;
+            assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0);
+            if guardsize == 0 {
+                if cfg!(all(target_os = "linux", target_env = "musl")) {
+                    // musl versions before 1.1.19 always reported guard
+                    // size obtained from pthread_attr_get_np as zero.
+                    // Use page size as a fallback.
+                    guardsize = PAGE_SIZE.load(Ordering::Relaxed);
+                } else {
+                    panic!("there is no guard page");
+                }
+            }
+            let mut stackptr = crate::ptr::null_mut::();
+            let mut size = 0;
+            assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0);
+
+            let stackaddr = stackptr.addr();
+            ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) {
+                Some(stackaddr - guardsize..stackaddr)
+            } else if cfg!(all(target_os = "linux", target_env = "musl")) {
+                Some(stackaddr - guardsize..stackaddr)
+            } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
+            {
+                // glibc used to include the guard area within the stack, as noted in the BUGS
+                // section of `man pthread_attr_getguardsize`. This has been corrected starting
+                // with glibc 2.27, and in some distro backports, so the guard is now placed at the
+                // end (below) the stack. There's no easy way for us to know which we have at
+                // runtime, so we'll just match any fault in the range right above or below the
+                // stack base to call that fault a stack overflow.
+                Some(stackaddr - guardsize..stackaddr + guardsize)
+            } else {
+                Some(stackaddr..stackaddr + guardsize)
+            };
+        }
+        if e == 0 || cfg!(target_os = "freebsd") {
+            assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+        }
+        ret
+    }
 }
 
 #[cfg(not(any(
     target_os = "linux",
-    target_os = "macos",
-    target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "hurd",
-    target_os = "solaris",
-    target_os = "illumos",
+    target_os = "macos",
     target_os = "netbsd",
     target_os = "openbsd",
+    target_os = "solaris"
 )))]
 mod imp {
     pub unsafe fn init() {}
 
     pub unsafe fn cleanup() {}
 
-    pub unsafe fn make_handler() -> super::Handler {
+    pub unsafe fn make_handler(_main_thread: bool) -> super::Handler {
         super::Handler::null()
     }
 
diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 4cd7c0e3059b..06b46b3c122f 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -729,16 +729,6 @@ mod cgroups {
     }
 }
 
-#[cfg(all(
-    not(target_os = "linux"),
-    not(target_os = "freebsd"),
-    not(target_os = "hurd"),
-    not(target_os = "macos"),
-    not(target_os = "netbsd"),
-    not(target_os = "openbsd"),
-    not(target_os = "solaris")
-))]
-#[cfg_attr(test, allow(dead_code))]
 pub mod guard {
     use crate::ops::Range;
     pub type Guard = Range;
@@ -750,281 +740,6 @@ pub mod guard {
     }
 }
 
-#[cfg(any(
-    target_os = "linux",
-    target_os = "freebsd",
-    target_os = "hurd",
-    target_os = "macos",
-    target_os = "netbsd",
-    target_os = "openbsd",
-    target_os = "solaris"
-))]
-#[cfg_attr(test, allow(dead_code))]
-pub mod guard {
-    #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
-    use libc::{mmap as mmap64, mprotect};
-    #[cfg(all(target_os = "linux", target_env = "gnu"))]
-    use libc::{mmap64, mprotect};
-    use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
-
-    use crate::io;
-    use crate::ops::Range;
-    use crate::sync::atomic::{AtomicUsize, Ordering};
-    use crate::sys::os;
-
-    // This is initialized in init() and only read from after
-    static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
-
-    pub type Guard = Range;
-
-    #[cfg(target_os = "solaris")]
-    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
-        let mut current_stack: libc::stack_t = crate::mem::zeroed();
-        assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
-        Some(current_stack.ss_sp)
-    }
-
-    #[cfg(target_os = "macos")]
-    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
-        let th = libc::pthread_self();
-        let stackptr = libc::pthread_get_stackaddr_np(th);
-        Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th)))
-    }
-
-    #[cfg(target_os = "openbsd")]
-    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
-        let mut current_stack: libc::stack_t = crate::mem::zeroed();
-        assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
-
-        let stack_ptr = current_stack.ss_sp;
-        let stackaddr = if libc::pthread_main_np() == 1 {
-            // main thread
-            stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
-        } else {
-            // new thread
-            stack_ptr.addr() - current_stack.ss_size
-        };
-        Some(stack_ptr.with_addr(stackaddr))
-    }
-
-    #[cfg(any(
-        target_os = "android",
-        target_os = "freebsd",
-        target_os = "netbsd",
-        target_os = "hurd",
-        target_os = "linux",
-        target_os = "l4re"
-    ))]
-    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
-        let mut ret = None;
-        let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
-        #[cfg(target_os = "freebsd")]
-        assert_eq!(libc::pthread_attr_init(&mut attr), 0);
-        #[cfg(target_os = "freebsd")]
-        let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
-        #[cfg(not(target_os = "freebsd"))]
-        let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
-        if e == 0 {
-            let mut stackaddr = crate::ptr::null_mut();
-            let mut stacksize = 0;
-            assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0);
-            ret = Some(stackaddr);
-        }
-        if e == 0 || cfg!(target_os = "freebsd") {
-            assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
-        }
-        ret
-    }
-
-    // Precondition: PAGE_SIZE is initialized.
-    unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> {
-        let page_size = PAGE_SIZE.load(Ordering::Relaxed);
-        assert!(page_size != 0);
-        let stackptr = get_stack_start()?;
-        let stackaddr = stackptr.addr();
-
-        // Ensure stackaddr is page aligned! A parent process might
-        // have reset RLIMIT_STACK to be non-page aligned. The
-        // pthread_attr_getstack() reports the usable stack area
-        // stackaddr < stackaddr + stacksize, so if stackaddr is not
-        // page-aligned, calculate the fix such that stackaddr <
-        // new_page_aligned_stackaddr < stackaddr + stacksize
-        let remainder = stackaddr % page_size;
-        Some(if remainder == 0 {
-            stackptr
-        } else {
-            stackptr.with_addr(stackaddr + page_size - remainder)
-        })
-    }
-
-    pub unsafe fn init() -> Option {
-        let page_size = os::page_size();
-        PAGE_SIZE.store(page_size, Ordering::Relaxed);
-
-        if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
-            // Linux doesn't allocate the whole stack right away, and
-            // the kernel has its own stack-guard mechanism to fault
-            // when growing too close to an existing mapping. If we map
-            // our own guard, then the kernel starts enforcing a rather
-            // large gap above that, rendering much of the possible
-            // stack space useless. See #43052.
-            //
-            // Instead, we'll just note where we expect rlimit to start
-            // faulting, so our handler can report "stack overflow", and
-            // trust that the kernel's own stack guard will work.
-            let stackptr = get_stack_start_aligned()?;
-            let stackaddr = stackptr.addr();
-            Some(stackaddr - page_size..stackaddr)
-        } else if cfg!(all(target_os = "linux", target_env = "musl")) {
-            // For the main thread, the musl's pthread_attr_getstack
-            // returns the current stack size, rather than maximum size
-            // it can eventually grow to. It cannot be used to determine
-            // the position of kernel's stack guard.
-            None
-        } else if cfg!(target_os = "freebsd") {
-            // FreeBSD's stack autogrows, and optionally includes a guard page
-            // at the bottom. If we try to remap the bottom of the stack
-            // ourselves, FreeBSD's guard page moves upwards. So we'll just use
-            // the builtin guard page.
-            let stackptr = get_stack_start_aligned()?;
-            let guardaddr = stackptr.addr();
-            // Technically the number of guard pages is tunable and controlled
-            // by the security.bsd.stack_guard_page sysctl.
-            // By default it is 1, checking once is enough since it is
-            // a boot time config value.
-            static LOCK: crate::sync::OnceLock = crate::sync::OnceLock::new();
-            let guard = guardaddr
-                ..guardaddr
-                    + *LOCK.get_or_init(|| {
-                        use crate::sys::weak::dlsym;
-                        dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int);
-                        let mut guard: usize = 0;
-                        let mut size = crate::mem::size_of_val(&guard);
-                        let oid = crate::ffi::CStr::from_bytes_with_nul(
-                            b"security.bsd.stack_guard_page\0",
-                        )
-                        .unwrap();
-                        match sysctlbyname.get() {
-                            Some(fcn) => {
-                                if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 {
-                                    return guard;
-                                }
-                                return 1;
-                            },
-                            _ => { return 1; }
-                        }
-                    }) * page_size;
-            Some(guard)
-        } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) {
-            // OpenBSD stack already includes a guard page, and stack is
-            // immutable.
-            // NetBSD stack includes the guard page.
-            //
-            // We'll just note where we expect rlimit to start
-            // faulting, so our handler can report "stack overflow", and
-            // trust that the kernel's own stack guard will work.
-            let stackptr = get_stack_start_aligned()?;
-            let stackaddr = stackptr.addr();
-            Some(stackaddr - page_size..stackaddr)
-        } else {
-            // Reallocate the last page of the stack.
-            // This ensures SIGBUS will be raised on
-            // stack overflow.
-            // Systems which enforce strict PAX MPROTECT do not allow
-            // to mprotect() a mapping with less restrictive permissions
-            // than the initial mmap() used, so we mmap() here with
-            // read/write permissions and only then mprotect() it to
-            // no permissions at all. See issue #50313.
-            let stackptr = get_stack_start_aligned()?;
-            let result = mmap64(
-                stackptr,
-                page_size,
-                PROT_READ | PROT_WRITE,
-                MAP_PRIVATE | MAP_ANON | MAP_FIXED,
-                -1,
-                0,
-            );
-            if result != stackptr || result == MAP_FAILED {
-                panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
-            }
-
-            let result = mprotect(stackptr, page_size, PROT_NONE);
-            if result != 0 {
-                panic!("failed to protect the guard page: {}", io::Error::last_os_error());
-            }
-
-            let guardaddr = stackptr.addr();
-
-            Some(guardaddr..guardaddr + page_size)
-        }
-    }
-
-    #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))]
-    pub unsafe fn current() -> Option {
-        let stackptr = get_stack_start()?;
-        let stackaddr = stackptr.addr();
-        Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
-    }
-
-    #[cfg(any(
-        target_os = "android",
-        target_os = "freebsd",
-        target_os = "hurd",
-        target_os = "linux",
-        target_os = "netbsd",
-        target_os = "l4re"
-    ))]
-    pub unsafe fn current() -> Option {
-        let mut ret = None;
-        let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
-        #[cfg(target_os = "freebsd")]
-        assert_eq!(libc::pthread_attr_init(&mut attr), 0);
-        #[cfg(target_os = "freebsd")]
-        let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
-        #[cfg(not(target_os = "freebsd"))]
-        let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
-        if e == 0 {
-            let mut guardsize = 0;
-            assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0);
-            if guardsize == 0 {
-                if cfg!(all(target_os = "linux", target_env = "musl")) {
-                    // musl versions before 1.1.19 always reported guard
-                    // size obtained from pthread_attr_get_np as zero.
-                    // Use page size as a fallback.
-                    guardsize = PAGE_SIZE.load(Ordering::Relaxed);
-                } else {
-                    panic!("there is no guard page");
-                }
-            }
-            let mut stackptr = crate::ptr::null_mut::();
-            let mut size = 0;
-            assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0);
-
-            let stackaddr = stackptr.addr();
-            ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) {
-                Some(stackaddr - guardsize..stackaddr)
-            } else if cfg!(all(target_os = "linux", target_env = "musl")) {
-                Some(stackaddr - guardsize..stackaddr)
-            } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
-            {
-                // glibc used to include the guard area within the stack, as noted in the BUGS
-                // section of `man pthread_attr_getguardsize`. This has been corrected starting
-                // with glibc 2.27, and in some distro backports, so the guard is now placed at the
-                // end (below) the stack. There's no easy way for us to know which we have at
-                // runtime, so we'll just match any fault in the range right above or below the
-                // stack base to call that fault a stack overflow.
-                Some(stackaddr - guardsize..stackaddr + guardsize)
-            } else {
-                Some(stackaddr..stackaddr + guardsize)
-            };
-        }
-        if e == 0 || cfg!(target_os = "freebsd") {
-            assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
-        }
-        ret
-    }
-}
-
 // glibc >= 2.15 has a __pthread_get_minstack() function that returns
 // PTHREAD_STACK_MIN plus bytes needed for thread-local storage.
 // We need that information to avoid blowing up when a small stack

From 76684181018640df1769604cd92ed6beb30a27d6 Mon Sep 17 00:00:00 2001
From: joboet 
Date: Sun, 31 Mar 2024 11:28:24 +0200
Subject: [PATCH 127/192] std: move `thread::current` TLS variable out of
 `thread_info`

---
 library/std/src/panicking.rs                  |  3 +-
 library/std/src/rt.rs                         |  6 +--
 library/std/src/sys/pal/hermit/thread.rs      | 10 ----
 library/std/src/sys/pal/itron/thread.rs       | 10 ----
 library/std/src/sys/pal/sgx/thread.rs         | 10 ----
 library/std/src/sys/pal/teeos/thread.rs       | 12 -----
 library/std/src/sys/pal/uefi/thread.rs        | 10 ----
 library/std/src/sys/pal/unix/thread.rs        | 11 ----
 library/std/src/sys/pal/unsupported/thread.rs | 10 ----
 library/std/src/sys/pal/wasi/thread.rs        | 10 ----
 library/std/src/sys/pal/windows/thread.rs     | 11 ----
 library/std/src/sys/pal/xous/thread.rs        | 10 ----
 library/std/src/sys/sync/rwlock/queue.rs      |  6 +--
 library/std/src/sys_common/mod.rs             |  1 -
 library/std/src/sys_common/thread_info.rs     | 53 -------------------
 library/std/src/thread/mod.rs                 | 32 ++++++++---
 16 files changed, 29 insertions(+), 176 deletions(-)
 delete mode 100644 library/std/src/sys_common/thread_info.rs

diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 31dbe86b66c7..f46e1e171d25 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -21,7 +21,6 @@ use crate::sync::atomic::{AtomicBool, Ordering};
 use crate::sync::{PoisonError, RwLock};
 use crate::sys::stdio::panic_output;
 use crate::sys_common::backtrace;
-use crate::sys_common::thread_info;
 use crate::thread;
 
 #[cfg(not(test))]
@@ -256,7 +255,7 @@ fn default_hook(info: &PanicInfo<'_>) {
             None => "Box",
         },
     };
-    let thread = thread_info::current_thread();
+    let thread = thread::try_current();
     let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("");
 
     let write = |err: &mut dyn crate::io::Write| {
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index 335944845ae0..0bafd9f352e3 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -24,8 +24,7 @@ pub use core::panicking::{panic_display, panic_fmt};
 
 use crate::sync::Once;
 use crate::sys;
-use crate::sys_common::thread_info;
-use crate::thread::Thread;
+use crate::thread::{self, Thread};
 
 // Prints to the "panic output", depending on the platform this may be:
 // - the standard error output
@@ -96,13 +95,12 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
     unsafe {
         sys::init(argc, argv, sigpipe);
 
-        let main_guard = sys::thread::guard::init();
         // Next, set up the current Thread with the guard information we just
         // created. Note that this isn't necessary in general for new threads,
         // but we just do this to name the main thread and to give it correct
         // info about the stack bounds.
         let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main"))));
-        thread_info::set(main_guard, thread);
+        thread::set_current(thread);
     }
 }
 
diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs
index cf45b9c23962..c896261ea35c 100644
--- a/library/std/src/sys/pal/hermit/thread.rs
+++ b/library/std/src/sys/pal/hermit/thread.rs
@@ -104,13 +104,3 @@ impl Thread {
 pub fn available_parallelism() -> io::Result> {
     unsafe { Ok(NonZero::new_unchecked(abi::get_processor_count())) }
 }
-
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs
index 814a102dd09a..2cad75cc3156 100644
--- a/library/std/src/sys/pal/itron/thread.rs
+++ b/library/std/src/sys/pal/itron/thread.rs
@@ -312,16 +312,6 @@ impl Drop for Thread {
     }
 }
 
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
-
 /// Terminate and delete the specified task.
 ///
 /// This function will abort if `deleted_task` refers to the calling task.
diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs
index 77f68bf73348..ef07f6e6a263 100644
--- a/library/std/src/sys/pal/sgx/thread.rs
+++ b/library/std/src/sys/pal/sgx/thread.rs
@@ -149,13 +149,3 @@ impl Thread {
 pub fn available_parallelism() -> io::Result> {
     unsupported()
 }
-
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs
index b76bcf9bbb0a..fb4b74ba3c36 100644
--- a/library/std/src/sys/pal/teeos/thread.rs
+++ b/library/std/src/sys/pal/teeos/thread.rs
@@ -151,18 +151,6 @@ pub fn available_parallelism() -> io::Result> {
     ))
 }
 
-// stub
-pub mod guard {
-    use crate::ops::Range;
-    pub type Guard = Range;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
-
 fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
     libc::PTHREAD_STACK_MIN.try_into().expect("Infallible")
 }
diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs
index b3a4f9c53e36..ca7b1efc2699 100644
--- a/library/std/src/sys/pal/uefi/thread.rs
+++ b/library/std/src/sys/pal/uefi/thread.rs
@@ -52,13 +52,3 @@ pub fn available_parallelism() -> io::Result> {
     // UEFI is single threaded
     Ok(NonZero::new(1).unwrap())
 }
-
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 06b46b3c122f..badedaa8ae9b 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -729,17 +729,6 @@ mod cgroups {
     }
 }
 
-pub mod guard {
-    use crate::ops::Range;
-    pub type Guard = Range;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
-
 // glibc >= 2.15 has a __pthread_get_minstack() function that returns
 // PTHREAD_STACK_MIN plus bytes needed for thread-local storage.
 // We need that information to avoid blowing up when a small stack
diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs
index b3a91ee1d4cb..d3f2fa35b929 100644
--- a/library/std/src/sys/pal/unsupported/thread.rs
+++ b/library/std/src/sys/pal/unsupported/thread.rs
@@ -38,13 +38,3 @@ impl Thread {
 pub fn available_parallelism() -> io::Result> {
     unsupported()
 }
-
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs
index 4b116052f8f8..940f0c8423af 100644
--- a/library/std/src/sys/pal/wasi/thread.rs
+++ b/library/std/src/sys/pal/wasi/thread.rs
@@ -193,13 +193,3 @@ impl Thread {
 pub fn available_parallelism() -> io::Result> {
     unsupported()
 }
-
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs
index 970bd9c6ce7e..80eee4e078db 100644
--- a/library/std/src/sys/pal/windows/thread.rs
+++ b/library/std/src/sys/pal/windows/thread.rs
@@ -144,14 +144,3 @@ pub fn available_parallelism() -> io::Result> {
         cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }),
     }
 }
-
-#[cfg_attr(test, allow(dead_code))]
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs
index f95ceb7343bd..c1fd1c0d6534 100644
--- a/library/std/src/sys/pal/xous/thread.rs
+++ b/library/std/src/sys/pal/xous/thread.rs
@@ -140,13 +140,3 @@ pub fn available_parallelism() -> io::Result> {
     // We're unicore right now.
     Ok(unsafe { NonZero::new_unchecked(1) })
 }
-
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option {
-        None
-    }
-    pub unsafe fn init() -> Option {
-        None
-    }
-}
diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs
index dce966086b8f..d1918855797a 100644
--- a/library/std/src/sys/sync/rwlock/queue.rs
+++ b/library/std/src/sys/sync/rwlock/queue.rs
@@ -115,8 +115,7 @@ use crate::sync::atomic::{
     AtomicBool, AtomicPtr,
     Ordering::{AcqRel, Acquire, Relaxed, Release},
 };
-use crate::sys_common::thread_info;
-use crate::thread::Thread;
+use crate::thread::{self, Thread};
 
 // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the
 // locking operation will be retried.
@@ -203,8 +202,7 @@ impl Node {
     fn prepare(&mut self) {
         // Fall back to creating an unnamed `Thread` handle to allow locking in
         // TLS destructors.
-        self.thread
-            .get_or_init(|| thread_info::current_thread().unwrap_or_else(|| Thread::new(None)));
+        self.thread.get_or_init(|| thread::try_current().unwrap_or_else(|| Thread::new(None)));
         self.completed = AtomicBool::new(false);
     }
 
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index 5410f135a73f..5abf201aa201 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -26,7 +26,6 @@ pub mod io;
 pub mod lazy_box;
 pub mod process;
 pub mod thread;
-pub mod thread_info;
 pub mod thread_local_dtor;
 pub mod thread_parking;
 pub mod wstr;
diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs
deleted file mode 100644
index ec1428ea40ec..000000000000
--- a/library/std/src/sys_common/thread_info.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-#![allow(dead_code)] // stack_guard isn't used right now on all platforms
-
-use crate::cell::OnceCell;
-use crate::sys;
-use crate::sys::thread::guard::Guard;
-use crate::thread::Thread;
-
-struct ThreadInfo {
-    stack_guard: OnceCell,
-    thread: OnceCell,
-}
-
-thread_local! {
-   static THREAD_INFO: ThreadInfo = const { ThreadInfo {
-       stack_guard: OnceCell::new(),
-       thread: OnceCell::new()
-   } };
-}
-
-impl ThreadInfo {
-    fn with(f: F) -> Option
-    where
-        F: FnOnce(&Thread, &OnceCell) -> R,
-    {
-        THREAD_INFO
-            .try_with(move |thread_info| {
-                let thread =
-                    thread_info.thread.get_or_init(|| Thread::new(sys::thread::Thread::get_name()));
-                f(thread, &thread_info.stack_guard)
-            })
-            .ok()
-    }
-}
-
-pub fn current_thread() -> Option {
-    ThreadInfo::with(|thread, _| thread.clone())
-}
-
-pub fn stack_guard() -> Option {
-    ThreadInfo::with(|_, guard| guard.get().cloned()).flatten()
-}
-
-/// Set new thread info, panicking if it has already been initialized
-#[allow(unreachable_code, unreachable_patterns)] // some platforms don't use stack_guard
-pub fn set(stack_guard: Option, thread: Thread) {
-    THREAD_INFO.with(move |thread_info| {
-        rtassert!(thread_info.stack_guard.get().is_none() && thread_info.thread.get().is_none());
-        if let Some(guard) = stack_guard {
-            thread_info.stack_guard.set(guard).unwrap();
-        }
-        thread_info.thread.set(thread).unwrap();
-    });
-}
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 85de2980133d..f7eb92bc61e2 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -159,7 +159,7 @@
 mod tests;
 
 use crate::any::Any;
-use crate::cell::UnsafeCell;
+use crate::cell::{OnceCell, UnsafeCell};
 use crate::ffi::{CStr, CString};
 use crate::fmt;
 use crate::io;
@@ -174,7 +174,6 @@ use crate::str;
 use crate::sync::Arc;
 use crate::sys::thread as imp;
 use crate::sys_common::thread;
-use crate::sys_common::thread_info;
 use crate::sys_common::thread_parking::Parker;
 use crate::sys_common::{AsInner, IntoInner};
 use crate::time::{Duration, Instant};
@@ -518,12 +517,8 @@ impl Builder {
 
             crate::io::set_output_capture(output_capture);
 
-            // SAFETY: we constructed `f` initialized.
             let f = f.into_inner();
-            // SAFETY: the stack guard passed is the one for the current thread.
-            // This means the current thread's stack and the new thread's stack
-            // are properly set and protected from each other.
-            thread_info::set(unsafe { imp::guard::current() }, their_thread);
+            set_current(their_thread);
             let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
                 crate::sys_common::backtrace::__rust_begin_short_backtrace(f)
             }));
@@ -683,6 +678,27 @@ where
     Builder::new().spawn(f).expect("failed to spawn thread")
 }
 
+thread_local! {
+    static CURRENT: OnceCell = const { OnceCell::new() };
+}
+
+/// Sets the thread handle for the current thread.
+///
+/// Panics if the handle has been set already or when called from a TLS destructor.
+pub(crate) fn set_current(thread: Thread) {
+    CURRENT.with(|current| current.set(thread).unwrap());
+}
+
+/// Gets a handle to the thread that invokes it.
+///
+/// In contrast to the public `current` function, this will not panic if called
+/// from inside a TLS destructor.
+pub(crate) fn try_current() -> Option {
+    CURRENT
+        .try_with(|current| current.get_or_init(|| Thread::new(imp::Thread::get_name())).clone())
+        .ok()
+}
+
 /// Gets a handle to the thread that invokes it.
 ///
 /// # Examples
@@ -705,7 +721,7 @@ where
 #[must_use]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn current() -> Thread {
-    thread_info::current_thread().expect(
+    try_current().expect(
         "use of std::thread::current() is not possible \
          after the thread's local data has been destroyed",
     )

From 42972f52de2aedbc8cc96a3f4b28c4330822aecd Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sun, 31 Mar 2024 11:54:16 +0200
Subject: [PATCH 128/192] catch_panic: warn about panicking payload drop

---
 library/std/src/panic.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs
index 3d576af681e0..e63b46ab7054 100644
--- a/library/std/src/panic.rs
+++ b/library/std/src/panic.rs
@@ -126,6 +126,9 @@ where
 /// Also note that unwinding into Rust code with a foreign exception (e.g.
 /// an exception thrown from C++ code) is undefined behavior.
 ///
+/// Finally, be **careful in how you drop the result of this function**.
+/// If it is `Err`, it contains the panic payload, and dropping that may in turn panic!
+///
 /// # Examples
 ///
 /// ```

From 6f4f39a8d56968a1ea120e6903c0640eb2a13ee9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= 
Date: Sat, 30 Mar 2024 19:56:08 +0100
Subject: [PATCH 129/192] Move submodule lookup to `Builder`

---
 src/bootstrap/src/core/builder.rs | 55 ++++++++++++++++++-------------
 1 file changed, 32 insertions(+), 23 deletions(-)

diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 7f93fdc72ef0..e9906ebd8360 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -554,29 +554,7 @@ impl<'a> ShouldRun<'a> {
     ///
     /// [`path`]: ShouldRun::path
     pub fn paths(mut self, paths: &[&str]) -> Self {
-        static SUBMODULES_PATHS: OnceLock> = OnceLock::new();
-
-        let init_submodules_paths = |src: &PathBuf| {
-            let file = File::open(src.join(".gitmodules")).unwrap();
-
-            let mut submodules_paths = vec![];
-            for line in BufReader::new(file).lines() {
-                if let Ok(line) = line {
-                    let line = line.trim();
-
-                    if line.starts_with("path") {
-                        let actual_path =
-                            line.split(' ').last().expect("Couldn't get value of path");
-                        submodules_paths.push(actual_path.to_owned());
-                    }
-                }
-            }
-
-            submodules_paths
-        };
-
-        let submodules_paths =
-            SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.builder.src));
+        let submodules_paths = self.builder.get_all_submodules();
 
         self.paths.insert(PathSet::Set(
             paths
@@ -2151,6 +2129,37 @@ impl<'a> Builder<'a> {
         out
     }
 
+    /// Return paths of all submodules managed by git.
+    /// If the current checkout is not managed by git, returns an empty slice.
+    pub fn get_all_submodules(&self) -> &[String] {
+        if !self.rust_info().is_managed_git_subrepository() {
+            return &[];
+        }
+
+        static SUBMODULES_PATHS: OnceLock> = OnceLock::new();
+
+        let init_submodules_paths = |src: &PathBuf| {
+            let file = File::open(src.join(".gitmodules")).unwrap();
+
+            let mut submodules_paths = vec![];
+            for line in BufReader::new(file).lines() {
+                if let Ok(line) = line {
+                    let line = line.trim();
+
+                    if line.starts_with("path") {
+                        let actual_path =
+                            line.split(' ').last().expect("Couldn't get value of path");
+                        submodules_paths.push(actual_path.to_owned());
+                    }
+                }
+            }
+
+            submodules_paths
+        };
+
+        &SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src))
+    }
+
     /// Ensure that a given step is built *only if it's supposed to be built by default*, returning
     /// its output. This will cache the step, so it's safe (and good!) to call this as often as
     /// needed to ensure that all dependencies are build.

From 63d6ce03b341ba2c42782099dcfae90e3daa5021 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= 
Date: Sat, 30 Mar 2024 19:58:30 +0100
Subject: [PATCH 130/192] Checkout all submodules when building source tarballs

---
 src/bootstrap/src/core/build_steps/dist.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index d9c7032d0db8..08d05920dc4b 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -995,9 +995,9 @@ impl Step for PlainSourceTarball {
         if builder.rust_info().is_managed_git_subrepository()
             || builder.rust_info().is_from_tarball()
         {
-            if builder.rust_info().is_managed_git_subrepository() {
-                // Ensure we have the submodules checked out.
-                builder.update_submodule(Path::new("src/tools/cargo"));
+            // Ensure we have all submodules from src and other directories checked out.
+            for submodule in builder.get_all_submodules() {
+                builder.update_submodule(Path::new(submodule));
             }
 
             // Vendor all Cargo dependencies

From 8caef4e6c30cd89496ab077e271cd0e9c03fe970 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= 
Date: Sat, 30 Mar 2024 21:21:58 +0100
Subject: [PATCH 131/192] Remove potential `__pycache__` directories from src
 tarballs

---
 src/bootstrap/src/core/build_steps/dist.rs | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 08d05920dc4b..ecea62140a2e 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -1028,6 +1028,20 @@ impl Step for PlainSourceTarball {
             builder.create(&cargo_config_dir.join("config.toml"), &config);
         }
 
+        // Delete extraneous directories
+        // FIXME: if we're managed by git, we should probably instead ask git if the given path
+        // is managed by it?
+        for entry in walkdir::WalkDir::new(tarball.image_dir())
+            .follow_links(true)
+            .into_iter()
+            .filter_map(|e| e.ok())
+        {
+            if entry.path().is_dir() && entry.path().file_name() == Some(OsStr::new("__pycache__"))
+            {
+                t!(fs::remove_dir_all(entry.path()));
+            }
+        }
+
         tarball.bare()
     }
 }

From 18d9d44bd6f431db4e50f5636b08458a8a1a63c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= 
Date: Sat, 30 Mar 2024 21:27:58 +0100
Subject: [PATCH 132/192] Make tarball generation more deterministic

---
 src/tools/rust-installer/src/tarballer.rs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-installer/src/tarballer.rs b/src/tools/rust-installer/src/tarballer.rs
index e5a925b2cbf2..24d341db78e5 100644
--- a/src/tools/rust-installer/src/tarballer.rs
+++ b/src/tools/rust-installer/src/tarballer.rs
@@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result};
 use std::fs::{read_link, symlink_metadata};
 use std::io::{BufWriter, Write};
 use std::path::Path;
-use tar::{Builder, Header};
+use tar::{Builder, Header, HeaderMode};
 use walkdir::WalkDir;
 
 use crate::{
@@ -61,6 +61,8 @@ impl Tarballer {
         // first, so files may be directly created. (See rust-lang/rustup.rs#1092.)
         let buf = BufWriter::with_capacity(1024 * 1024, encoder);
         let mut builder = Builder::new(buf);
+        // Make uid, gid and mtime deterministic to improve reproducibility
+        builder.mode(HeaderMode::Deterministic);
 
         let pool = rayon::ThreadPoolBuilder::new().num_threads(2).build().unwrap();
         pool.install(move || {
@@ -91,7 +93,8 @@ impl Tarballer {
 fn append_path(builder: &mut Builder, src: &Path, path: &String) -> Result<()> {
     let stat = symlink_metadata(src)?;
     let mut header = Header::new_gnu();
-    header.set_metadata(&stat);
+    header.set_metadata_in_mode(&stat, HeaderMode::Deterministic);
+
     if stat.file_type().is_symlink() {
         let link = read_link(src)?;
         builder.append_link(&mut header, path, &link)?;

From 877e8d456db497c2b7b895cb88243fb894584701 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= 
Date: Sat, 30 Mar 2024 22:03:11 +0100
Subject: [PATCH 133/192] Sort directories when generating tarballs

---
 src/tools/rust-installer/src/tarballer.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-installer/src/tarballer.rs b/src/tools/rust-installer/src/tarballer.rs
index 24d341db78e5..2f093e7ad17f 100644
--- a/src/tools/rust-installer/src/tarballer.rs
+++ b/src/tools/rust-installer/src/tarballer.rs
@@ -53,8 +53,11 @@ impl Tarballer {
         // Sort files by their suffix, to group files with the same name from
         // different locations (likely identical) and files with the same
         // extension (likely containing similar data).
-        let (dirs, mut files) = get_recursive_paths(&self.work_dir, &self.input)
+        // Sorting of file and directory paths also helps with the reproducibility
+        // of the resulting archive.
+        let (mut dirs, mut files) = get_recursive_paths(&self.work_dir, &self.input)
             .context("failed to collect file paths")?;
+        dirs.sort();
         files.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
 
         // Write the tar into both encoded files. We write all directories

From 4ca315156805d951663e66c13f395d7f405c9ed2 Mon Sep 17 00:00:00 2001
From: Jani Mustonen 
Date: Sun, 31 Mar 2024 16:05:34 +0300
Subject: [PATCH 134/192] doc: describe panic conditions for SliceIndex
 implementations

Implementation note: The most probable place for users to find
the documentation is at https://doc.rust-lang.org/std/slice/trait.SliceIndex.html

On that page, documentation added to specific methods will not
be visible. As such, I opted to add the comments to the impl blocks
directly.

Helps with #121568.
---
 library/core/src/slice/index.rs | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs
index 127a407dae5d..8d7b6165510a 100644
--- a/library/core/src/slice/index.rs
+++ b/library/core/src/slice/index.rs
@@ -195,6 +195,7 @@ pub unsafe trait SliceIndex: private_slice_index::Sealed {
     fn index_mut(self, slice: &mut T) -> &mut Self::Output;
 }
 
+/// The methods `index` and `index_mut` panic if the index is out of bounds.
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
 #[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
 unsafe impl SliceIndex<[T]> for usize {
@@ -328,6 +329,9 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange {
     }
 }
 
+/// The methods `index` and `index_mut` panic if:
+/// - the start of the range is greater than the end of the range or
+/// - the end of the range is out of bounds.
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
 #[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
 unsafe impl SliceIndex<[T]> for ops::Range {
@@ -416,6 +420,7 @@ unsafe impl SliceIndex<[T]> for ops::Range {
     }
 }
 
+/// The methods `index` and `index_mut` panic if the end of the range is out of bounds.
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
 #[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
 unsafe impl SliceIndex<[T]> for ops::RangeTo {
@@ -454,6 +459,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeTo {
     }
 }
 
+/// The methods `index` and `index_mut` panic if the start of the range is out of bounds.
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
 #[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
 unsafe impl SliceIndex<[T]> for ops::RangeFrom {
@@ -536,6 +542,10 @@ unsafe impl SliceIndex<[T]> for ops::RangeFull {
     }
 }
 
+/// The methods `index` and `index_mut` panic if:
+/// - the end of the range is `usize::MAX` or
+/// - the start of the range is greater than the end of the range or
+/// - the end of the range is out of bounds.
 #[stable(feature = "inclusive_range", since = "1.26.0")]
 #[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
 unsafe impl SliceIndex<[T]> for ops::RangeInclusive {
@@ -580,6 +590,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive {
     }
 }
 
+/// The methods `index` and `index_mut` panic if the end of the range is out of bounds.
 #[stable(feature = "inclusive_range", since = "1.26.0")]
 #[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
 unsafe impl SliceIndex<[T]> for ops::RangeToInclusive {

From 41434ff4a3592f5ae9edcb057dca4ae09824eac7 Mon Sep 17 00:00:00 2001
From: joboet 
Date: Sun, 31 Mar 2024 15:38:22 +0200
Subject: [PATCH 135/192] refer to a different module in UI test

---
 .../stability-in-private-module.rs               |  4 ++--
 .../stability-in-private-module.stderr           | 16 ++++++++--------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/tests/ui/stability-attribute/stability-in-private-module.rs b/tests/ui/stability-attribute/stability-in-private-module.rs
index f12e9198b0d7..df94931690b7 100644
--- a/tests/ui/stability-attribute/stability-in-private-module.rs
+++ b/tests/ui/stability-attribute/stability-in-private-module.rs
@@ -1,4 +1,4 @@
 fn main() {
-    let _ = std::thread::thread_info::current_thread();
-    //~^ERROR module `thread_info` is private
+    let _ = std::sys::os::errno();
+    //~^ERROR module `sys` is private
 }
diff --git a/tests/ui/stability-attribute/stability-in-private-module.stderr b/tests/ui/stability-attribute/stability-in-private-module.stderr
index 9eb4d3efc8bd..e65f8aa9b1fc 100644
--- a/tests/ui/stability-attribute/stability-in-private-module.stderr
+++ b/tests/ui/stability-attribute/stability-in-private-module.stderr
@@ -1,13 +1,13 @@
-error[E0603]: module `thread_info` is private
-  --> $DIR/stability-in-private-module.rs:2:26
+error[E0603]: module `sys` is private
+  --> $DIR/stability-in-private-module.rs:2:18
    |
-LL |     let _ = std::thread::thread_info::current_thread();
-   |                          ^^^^^^^^^^^  -------------- function `current_thread` is not publicly re-exported
-   |                          |
-   |                          private module
+LL |     let _ = std::sys::os::errno();
+   |                  ^^^      ----- function `errno` is not publicly re-exported
+   |                  |
+   |                  private module
    |
-note: the module `thread_info` is defined here
-  --> $SRC_DIR/std/src/thread/mod.rs:LL:COL
+note: the module `sys` is defined here
+  --> $SRC_DIR/std/src/lib.rs:LL:COL
 
 error: aborting due to 1 previous error
 

From e5c5ed00a5e8ecf453dca2072d54e51316594a5e Mon Sep 17 00:00:00 2001
From: David Carlier 
Date: Sun, 31 Mar 2024 11:39:06 +0100
Subject: [PATCH 136/192] std::thread: adding get_name haiku implementation.

follow-up #123233
---
 library/std/src/sys/pal/unix/thread.rs | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 01e3c3cbc6fd..77e31d802a38 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -257,6 +257,23 @@ impl Thread {
         CString::new(name).ok()
     }
 
+    #[cfg(target_os = "haiku")]
+    pub fn get_name() -> Option {
+        unsafe {
+            let mut tinfo = mem::MaybeUninit::::uninit();
+            // See BeOS teams group and threads api.
+            // https://www.haiku-os.org/legacy-docs/bebook/TheKernelKit_ThreadsAndTeams_Overview.html
+            let thread_self = libc::find_thread(ptr::null_mut());
+            let res = libc::get_thread_info(thread_self, tinfo.as_mut_ptr());
+            if res != libc::B_OK {
+                return None;
+            }
+            let info = tinfo.assume_init();
+            let name = slice::from_raw_parts(info.name.as_ptr() as *const u8, info.name.len());
+            CStr::from_bytes_until_nul(name).map(CStr::to_owned).ok()
+        }
+    }
+
     #[cfg(not(any(
         target_os = "linux",
         target_os = "freebsd",
@@ -264,7 +281,8 @@ impl Thread {
         target_os = "macos",
         target_os = "ios",
         target_os = "tvos",
-        target_os = "watchos"
+        target_os = "watchos",
+        target_os = "haiku"
     )))]
     pub fn get_name() -> Option {
         None

From 4056df5cf74e83286417f0df83cba510c32488d3 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Fri, 29 Mar 2024 12:27:07 +0100
Subject: [PATCH 137/192] cargo-miri: better debug output; reorder a comment to
 make it less confusing

---
 src/tools/miri/cargo-miri/src/phases.rs | 10 +++++++---
 src/tools/miri/cargo-miri/src/setup.rs  |  6 +++---
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs
index 8c0f605fd6ec..18b2c9c3bd8e 100644
--- a/src/tools/miri/cargo-miri/src/phases.rs
+++ b/src/tools/miri/cargo-miri/src/phases.rs
@@ -89,8 +89,12 @@ pub fn phase_cargo_miri(mut args: impl Iterator) {
     let verbose = num_arg_flag("-v");
 
     // Determine the involved architectures.
-    let rustc_version = VersionMeta::for_command(miri_for_host())
-        .expect("failed to determine underlying rustc version of Miri");
+    let rustc_version = VersionMeta::for_command(miri_for_host()).unwrap_or_else(|err| {
+        panic!(
+            "failed to determine underlying rustc version of Miri ({:?}):\n{err:?}",
+            miri_for_host()
+        )
+    });
     let host = &rustc_version.host;
     let target = get_arg_flag_value("--target");
     let target = target.as_ref().unwrap_or(host);
@@ -213,7 +217,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) {
     }
 
     // Run cargo.
-    debug_cmd("[cargo-miri miri]", verbose, &cmd);
+    debug_cmd("[cargo-miri cargo]", verbose, &cmd);
     exec(cmd)
 }
 
diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs
index a98e1fcd485e..401e9158faec 100644
--- a/src/tools/miri/cargo-miri/src/setup.rs
+++ b/src/tools/miri/cargo-miri/src/setup.rs
@@ -90,13 +90,13 @@ pub fn setup(
     let cargo_cmd = {
         let mut command = cargo();
         // Use Miri as rustc to build a libstd compatible with us (and use the right flags).
+        // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags
+        // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves).
+        // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`.
         // However, when we are running in bootstrap, we cannot just overwrite `RUSTC`,
         // because we still need bootstrap to distinguish between host and target crates.
         // In that case we overwrite `RUSTC_REAL` instead which determines the rustc used
         // for target crates.
-        // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags
-        // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves).
-        // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`.
         let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
         if env::var_os("RUSTC_STAGE").is_some() {
             assert!(env::var_os("RUSTC").is_some());

From b5fe655ae855c72d49f26103ec4717995c91ff02 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Fri, 29 Mar 2024 12:27:20 +0100
Subject: [PATCH 138/192] bootstrap/rustc: remove a miri hack

---
 src/bootstrap/src/bin/rustc.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 74a924d86c79..12251924717d 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -150,7 +150,7 @@ fn main() {
         {
             cmd.arg("-Ztls-model=initial-exec");
         }
-    } else if std::env::var("MIRI").is_err() {
+    } else {
         // Find any host flags that were passed by bootstrap.
         // The flags are stored in a RUSTC_HOST_FLAGS variable, separated by spaces.
         if let Ok(flags) = std::env::var("RUSTC_HOST_FLAGS") {

From fb8abe5fcfca0ad43279e7f62ad14b8604d62efc Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 30 Mar 2024 13:27:17 +0100
Subject: [PATCH 139/192] shift Miri's stage so that it matches other
 rustc-based tools

---
 src/bootstrap/src/core/build_steps/run.rs  | 26 +++++--
 src/bootstrap/src/core/build_steps/test.rs | 90 ++++++++++++----------
 src/bootstrap/src/core/builder.rs          | 11 +--
 3 files changed, 72 insertions(+), 55 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index bad51825235e..7028bffea544 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -121,7 +121,6 @@ impl Step for ReplaceVersionPlaceholder {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Miri {
-    host: TargetSelection,
     target: TargetSelection,
 }
 
@@ -134,24 +133,35 @@ impl Step for Miri {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Miri { host: run.build_triple(), target: run.target });
+        run.builder.ensure(Miri { target: run.target });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let stage = builder.top_stage;
-        let host = self.host;
+        let host = builder.build.build;
         let target = self.target;
-        let compiler = builder.compiler(stage, host);
+        let stage = builder.top_stage;
+        if stage == 0 {
+            eprintln!("miri cannot be run at stage 0");
+            std::process::exit(1);
+        }
 
-        let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host);
-        let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler_std, target);
+        // This compiler runs on the host, we'll just use it for the target.
+        let target_compiler = builder.compiler(stage, host);
+        // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
+        // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage
+        // compilers, which isn't what we want. Rustdoc should be linked in the same way as the
+        // rustc compiler it's paired with, so it must be built with the previous stage compiler.
+        let host_compiler = builder.compiler(stage - 1, host);
+
+        // Get a target sysroot for Miri.
+        let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target);
 
         // # Run miri.
         // Running it via `cargo run` as that figures out the right dylib path.
         // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so.
         let mut miri = tool::prepare_tool_cargo(
             builder,
-            compiler,
+            host_compiler,
             Mode::ToolRustc,
             host,
             "run",
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 2561f58e3562..c3fab96ef26e 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -493,7 +493,6 @@ impl Step for RustDemangler {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Miri {
-    host: TargetSelection,
     target: TargetSelection,
 }
 
@@ -501,13 +500,13 @@ impl Miri {
     /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put.
     pub fn build_miri_sysroot(
         builder: &Builder<'_>,
-        compiler_std: Compiler,
+        compiler: Compiler,
         target: TargetSelection,
     ) -> String {
-        let miri_sysroot = builder.out.join(compiler_std.host.triple).join("miri-sysroot");
+        let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot");
         let mut cargo = builder::Cargo::new(
             builder,
-            compiler_std, // this is compiler+1; cargo_miri_cmd will do -1 again
+            compiler,
             Mode::Std,
             SourceType::Submodule,
             target,
@@ -520,13 +519,8 @@ impl Miri {
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
 
         let mut cargo = Command::from(cargo);
-        let _guard = builder.msg(
-            Kind::Build,
-            compiler_std.stage,
-            "miri sysroot",
-            compiler_std.host,
-            compiler_std.host,
-        );
+        let _guard =
+            builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target);
         builder.run(&mut cargo);
 
         // # Determine where Miri put its sysroot.
@@ -563,34 +557,51 @@ impl Step for Miri {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Miri { host: run.build_triple(), target: run.target });
+        run.builder.ensure(Miri { target: run.target });
     }
 
     /// Runs `cargo test` for miri.
     fn run(self, builder: &Builder<'_>) {
-        let stage = builder.top_stage;
-        let host = self.host;
+        let host = builder.build.build;
         let target = self.target;
-        let compiler = builder.compiler(stage, host);
-        // We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri.
-        // Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage.
-        let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host);
+        let stage = builder.top_stage;
+        if stage == 0 {
+            eprintln!("miri cannot be tested at stage 0");
+            std::process::exit(1);
+        }
 
-        let miri =
-            builder.ensure(tool::Miri { compiler, target: host, extra_features: Vec::new() });
+        // This compiler runs on the host, we'll just use it for the target.
+        let target_compiler = builder.compiler(stage, host);
+        // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
+        // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage
+        // compilers, which isn't what we want. Rustdoc should be linked in the same way as the
+        // rustc compiler it's paired with, so it must be built with the previous stage compiler.
+        let host_compiler = builder.compiler(stage - 1, host);
+
+        // Build our tools.
+        let miri = builder.ensure(tool::Miri {
+            compiler: host_compiler,
+            target: host,
+            extra_features: Vec::new(),
+        });
         // the ui tests also assume cargo-miri has been built
-        builder.ensure(tool::CargoMiri { compiler, target: host, extra_features: Vec::new() });
-        // The stdlib we need might be at a different stage. And just asking for the
-        // sysroot does not seem to populate it, so we do that first.
-        builder.ensure(compile::Std::new(compiler_std, host));
-        let sysroot = builder.sysroot(compiler_std);
-        // We also need a Miri sysroot.
-        let miri_sysroot = Miri::build_miri_sysroot(builder, compiler_std, target);
+        builder.ensure(tool::CargoMiri {
+            compiler: host_compiler,
+            target: host,
+            extra_features: Vec::new(),
+        });
+
+        // We also need sysroots, for Miri and for the host (the latter for build scripts).
+        // This is for the tests so everything is done with the target compiler.
+        let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target);
+        builder.ensure(compile::Std::new(target_compiler, host));
+        let sysroot = builder.sysroot(target_compiler);
 
         // # Run `cargo test`.
+        // This is with the Miri crate, so it uses the host compiler.
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            compiler,
+            host_compiler,
             Mode::ToolRustc,
             host,
             "test",
@@ -603,7 +614,7 @@ impl Step for Miri {
 
         // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test
         // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`.
-        let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder);
+        let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host_compiler, host, builder);
 
         // miri tests need to know about the stage sysroot
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
@@ -618,7 +629,7 @@ impl Step for Miri {
         cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg());
 
         {
-            let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target);
+            let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target);
             let _time = helpers::timeit(builder);
             builder.run(&mut cargo);
         }
@@ -636,7 +647,7 @@ impl Step for Miri {
             {
                 let _guard = builder.msg_sysroot_tool(
                     Kind::Test,
-                    compiler.stage,
+                    stage,
                     "miri (mir-opt-level 4)",
                     host,
                     target,
@@ -650,10 +661,10 @@ impl Step for Miri {
         // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures
         // that we get the desired output), but that is sufficient to make sure that the libtest harness
         // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode.
-        // Everything here needs `compiler_std` to be actually testing the Miri in the current stage.
+        // This is running the build `cargo-miri` for the given target, so we need the target compiler.
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            compiler_std,  // this is compiler+1; cargo_miri_cmd will do -1 again
+            target_compiler,
             Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test!
             target,
             "miri-test",
@@ -662,16 +673,12 @@ impl Step for Miri {
             &[],
         );
 
-        // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "miri", not a "test".
-        // Also, we want the rustdoc from the "next" stage for the same reason that we build a std from the next stage.
-        // So let's just set that here, and bypass bootstrap's RUSTDOC (just like cargo-miri already ignores bootstrap's RUSTC_WRAPPER).
-        if builder.doc_tests != DocTests::No {
-            cargo.env("RUSTDOC", builder.rustdoc(compiler_std));
-        }
+        // We're not using `prepare_cargo_test` so we have to do this ourselves.
+        // (We're not using that as the test-cargo-miri crate is not known to bootstrap.)
         match builder.doc_tests {
             DocTests::Yes => {}
             DocTests::No => {
-                cargo.arg("--tests");
+                cargo.args(["--lib", "--bins", "--examples", "--tests", "--benches"]);
             }
             DocTests::Only => {
                 cargo.arg("--doc");
@@ -686,8 +693,7 @@ impl Step for Miri {
         cargo.arg("--").args(builder.config.test_args());
         let mut cargo = Command::from(cargo);
         {
-            let _guard =
-                builder.msg_sysroot_tool(Kind::Test, compiler.stage, "cargo-miri", host, target);
+            let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target);
             let _time = helpers::timeit(builder);
             builder.run(&mut cargo);
         }
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 8294abcb4eb7..c97d8121e0ca 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1031,10 +1031,10 @@ impl<'a> Builder<'a> {
         StepDescription::run(v, self, paths);
     }
 
-    /// Obtain a compiler at a given stage and for a given host. Explicitly does
-    /// not take `Compiler` since all `Compiler` instances are meant to be
-    /// obtained through this function, since it ensures that they are valid
-    /// (i.e., built and assembled).
+    /// Obtain a compiler at a given stage and for a given host (i.e., this is the target that the
+    /// compiler will run on, *not* the target it will build code for). Explicitly does not take
+    /// `Compiler` since all `Compiler` instances are meant to be obtained through this function,
+    /// since it ensures that they are valid (i.e., built and assembled).
     pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler {
         self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } })
     }
@@ -1703,7 +1703,8 @@ impl<'a> Builder<'a> {
             .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
             .env(
                 "RUSTDOC_REAL",
-                if cmd == "doc" || cmd == "rustdoc" || (cmd == "test" && want_rustdoc) {
+                // Make sure to handle both `test` and `miri-test` commands.
+                if cmd == "doc" || cmd == "rustdoc" || (cmd.ends_with("test") && want_rustdoc) {
                     self.rustdoc(compiler)
                 } else {
                     PathBuf::from("/path/to/nowhere/rustdoc/not/required")

From 7ac5f604c1601c0c43cd305ae43c78795c1eac2b Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 30 Mar 2024 13:37:32 +0100
Subject: [PATCH 140/192] remove a pointless env var

CARGO_EXTRA_FLAGS is respected by the ./miri script which we are not invoking here
---
 src/bootstrap/src/core/build_steps/test.rs | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index c3fab96ef26e..8a79c28fc6c6 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -620,10 +620,6 @@ impl Step for Miri {
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
         cargo.env("MIRI_HOST_SYSROOT", &sysroot);
         cargo.env("MIRI", &miri);
-        if builder.config.locked_deps {
-            // enforce lockfiles
-            cargo.env("CARGO_EXTRA_FLAGS", "--locked");
-        }
 
         // Set the target.
         cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg());

From 288daeb14f70192d53d2331b4c43a8c6d90d9d7e Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 30 Mar 2024 13:52:32 +0100
Subject: [PATCH 141/192] move parallel_compiler handling into
 prepare_tool_cargo so that it is done everywhere

---
 src/bootstrap/src/core/build_steps/compile.rs |  6 ------
 src/bootstrap/src/core/build_steps/tool.rs    | 12 ++----------
 src/bootstrap/src/core/builder.rs             |  9 +++++++++
 3 files changed, 11 insertions(+), 16 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index d40a3ea4c884..5bb7dc5b482d 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1130,12 +1130,6 @@ pub fn rustc_cargo_env(
         cargo.env("CFG_DEFAULT_LINKER", s);
     }
 
-    if builder.config.rustc_parallel {
-        // keep in sync with `bootstrap/lib.rs:Build::rustc_features`
-        // `cfg` option for rustc, `features` option for cargo, for conditional compilation
-        cargo.rustflag("--cfg=parallel_compiler");
-        cargo.rustdocflag("--cfg=parallel_compiler");
-    }
     if builder.config.rust_verify_llvm_ir {
         cargo.env("RUSTC_VERIFY_LLVM_IR", "1");
     }
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index deab3fce54ca..1edf65d28b76 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -469,7 +469,7 @@ impl Step for Rustdoc {
             features.push("jemalloc".to_string());
         }
 
-        let mut cargo = prepare_tool_cargo(
+        let cargo = prepare_tool_cargo(
             builder,
             build_compiler,
             Mode::ToolRustc,
@@ -480,10 +480,6 @@ impl Step for Rustdoc {
             features.as_slice(),
         );
 
-        if builder.config.rustc_parallel {
-            cargo.rustflag("--cfg=parallel_compiler");
-        }
-
         let _guard = builder.msg_tool(
             Mode::ToolRustc,
             "rustdoc",
@@ -732,7 +728,7 @@ impl Step for LlvmBitcodeLinker {
         builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
         builder.ensure(compile::Rustc::new(self.compiler, self.target));
 
-        let mut cargo = prepare_tool_cargo(
+        let cargo = prepare_tool_cargo(
             builder,
             self.compiler,
             Mode::ToolRustc,
@@ -743,10 +739,6 @@ impl Step for LlvmBitcodeLinker {
             &self.extra_features,
         );
 
-        if builder.config.rustc_parallel {
-            cargo.rustflag("--cfg=parallel_compiler");
-        }
-
         builder.run(&mut cargo.into());
 
         let tool_out = builder
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index c97d8121e0ca..4b2ee1d050ad 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -2100,6 +2100,15 @@ impl<'a> Builder<'a> {
             rustflags.arg("-Zinline-mir");
         }
 
+        if builder.config.rustc_parallel
+            && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen)
+        {
+            // keep in sync with `bootstrap/lib.rs:Build::rustc_features`
+            // `cfg` option for rustc, `features` option for cargo, for conditional compilation
+            rustflags.arg("--cfg=parallel_compiler");
+            rustdocflags.arg("--cfg=parallel_compiler");
+        }
+
         // set rustc args passed from command line
         let rustc_args =
             self.config.cmd.rustc_args().iter().map(|s| s.to_string()).collect::>();

From 4797fba3b71fc44b0904ebd52603a953609a9eb6 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 30 Mar 2024 15:09:38 +0100
Subject: [PATCH 142/192] add FIXME for making the cargo cmd properly typed

---
 src/bootstrap/src/core/builder.rs | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 4b2ee1d050ad..4941f5a8ca59 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1318,7 +1318,7 @@ impl<'a> Builder<'a> {
         compiler: Compiler,
         mode: Mode,
         target: TargetSelection,
-        cmd: &str,
+        cmd: &str, // FIXME make this properly typed
     ) -> Command {
         let mut cargo;
         if cmd == "clippy" {
@@ -1392,7 +1392,7 @@ impl<'a> Builder<'a> {
         mode: Mode,
         source_type: SourceType,
         target: TargetSelection,
-        cmd: &str,
+        cmd: &str, // FIXME make this properly typed
     ) -> Cargo {
         let mut cargo = self.bare_cargo(compiler, mode, target, cmd);
         let out_dir = self.stage_out(compiler, mode);
@@ -2100,7 +2100,7 @@ impl<'a> Builder<'a> {
             rustflags.arg("-Zinline-mir");
         }
 
-        if builder.config.rustc_parallel
+        if self.config.rustc_parallel
             && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen)
         {
             // keep in sync with `bootstrap/lib.rs:Build::rustc_features`
@@ -2342,7 +2342,7 @@ impl Cargo {
         mode: Mode,
         source_type: SourceType,
         target: TargetSelection,
-        cmd: &str,
+        cmd: &str, // FIXME make this properly typed
     ) -> Cargo {
         let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd);
         cargo.configure_linker(builder);
@@ -2356,7 +2356,7 @@ impl Cargo {
         mode: Mode,
         source_type: SourceType,
         target: TargetSelection,
-        cmd: &str,
+        cmd: &str, // FIXME make this properly typed
     ) -> Cargo {
         builder.cargo(compiler, mode, source_type, target, cmd)
     }

From 8d67fb99fc1d4e93c441ac220fc75e19140615a0 Mon Sep 17 00:00:00 2001
From: Boxy 
Date: Sun, 31 Mar 2024 20:18:37 +0100
Subject: [PATCH 143/192] beep boop

---
 triagebot.toml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/triagebot.toml b/triagebot.toml
index 0df71c60d545..55f0d32398ff 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -788,6 +788,7 @@ compiler-team-contributors = [
     "@Nadrieril",
     "@fmease",
     "@fee1-dead",
+    "@BoxyUwU",
 ]
 compiler = [
     "compiler-team",

From 602401c4d49753dfd9d351ffcf0c72c00ff4c62f Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sun, 31 Mar 2024 12:46:37 +0200
Subject: [PATCH 144/192] warn against implementing Freeze

---
 library/core/src/marker.rs | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 385c288db12c..1d073a6d649b 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -817,6 +817,13 @@ pub trait DiscriminantKind {
 /// This can be used to declare that a constant with a generic type
 /// will not contain interior mutability, and subsequently allow
 /// placing the constant behind references.
+///
+/// # Safety
+///
+/// This trait is a core part of the language, it is just expressed as a trait in libcore for
+/// convenience. Do *not* implement it for other types.
+// FIXME: Eventually this trait should become `#[rustc_deny_explicit_impl]`.
+// That requires porting the impls below to native internal impls.
 #[lang = "freeze"]
 #[unstable(feature = "freeze", issue = "121675")]
 pub unsafe auto trait Freeze {}

From 418535b798946b29c70a3ac4e33bbc6fbb4fb132 Mon Sep 17 00:00:00 2001
From: Jani Mustonen 
Date: Mon, 1 Apr 2024 00:04:57 +0300
Subject: [PATCH 145/192] doc: mention heap allocation earlier in String docs

Just a tiny addition.

Helps with #123263.
---
 library/alloc/src/string.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 7464df268cc1..5d552c8f15c6 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -72,9 +72,9 @@ use crate::vec::Vec;
 
 /// A UTF-8–encoded, growable string.
 ///
-/// The `String` type is the most common string type that has ownership over the
-/// contents of the string. It has a close relationship with its borrowed
-/// counterpart, the primitive [`str`].
+/// `String` is the most common string type. It has ownership over the contents
+/// of the string, stored in a heap-allocated buffer (see [Representation](#representation)).
+/// It is closely related to its borrowed counterpart, the primitive [`str`].
 ///
 /// # Examples
 ///

From b08b06e3a861e237a5bd961a568291eb0cd4e833 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sun, 31 Mar 2024 23:09:33 +0200
Subject: [PATCH 146/192] fix not finding the right libraries on Windows

---
 src/bootstrap/src/core/builder.rs | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 4941f5a8ca59..8726d7c0812c 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1271,9 +1271,15 @@ impl<'a> Builder<'a> {
         let mut cmd = Command::new(cargo_miri);
         cmd.env("MIRI", &miri);
         cmd.env("CARGO", &self.initial_cargo);
-        // Need to add the run_compiler libs. Not entirely sure why that has to be one stage up from
-        // what Miri was built for.
-        self.add_rustc_lib_path(run_compiler, &mut cmd);
+        // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`,
+        // so they match the Miri we just built. However this means they are actually living one
+        // stage up, i.e. we are running `stage0-tools-bin/miri` with the libraries in `stage1/lib`.
+        // This is an unfortunate off-by-1 caused (possibly) by the fact that Miri doesn't have an
+        // "assemble" step like rustc does that would cross the stage boundary. We can't use
+        // `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries added to
+        // the PATH due to the stage mismatch.
+        // Also see https://github.com/rust-lang/rust/pull/123192#issuecomment-2028901503.
+        add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd);
         cmd
     }
 

From 85d460e829c5ca2600fed6b432a329dbf6d18689 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sun, 31 Mar 2024 23:10:47 +0200
Subject: [PATCH 147/192] checktools: make it easier to trace what is happening

---
 src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
index f5c426a717f7..10ae7f17db7a 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
@@ -2,6 +2,7 @@
 # ignore-tidy-linelength
 
 set -eu
+set -x # so one can see where we are in the script
 
 X_PY="$1"
 

From b8396d10c42cf758bcf901019181a0d47e56ed19 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 6 Oct 2023 20:27:30 +0000
Subject: [PATCH 148/192] Always make inductive cycles as ambig during typeck

---
 .../src/traits/select/mod.rs                  |  2 +-
 tests/ui/marker_trait_attr/unsound-overlap.rs |  1 +
 .../marker_trait_attr/unsound-overlap.stderr  | 21 +++++++++++--
 tests/ui/specialization/issue-39448.rs        |  3 +-
 tests/ui/specialization/issue-39448.stderr    | 31 +++++++------------
 tests/ui/specialization/issue-39618.rs        |  3 +-
 tests/ui/specialization/issue-39618.stderr    | 20 ++++++++++--
 7 files changed, 53 insertions(+), 28 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 1894fbba3027..81b665236889 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -236,7 +236,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             freshener: infcx.freshener(),
             intercrate_ambiguity_causes: None,
             query_mode: TraitQueryMode::Standard,
-            treat_inductive_cycle: TreatInductiveCycleAs::Recur,
+            treat_inductive_cycle: TreatInductiveCycleAs::Ambig,
         }
     }
 
diff --git a/tests/ui/marker_trait_attr/unsound-overlap.rs b/tests/ui/marker_trait_attr/unsound-overlap.rs
index 2e5101b822c0..2ce26b610f65 100644
--- a/tests/ui/marker_trait_attr/unsound-overlap.rs
+++ b/tests/ui/marker_trait_attr/unsound-overlap.rs
@@ -8,6 +8,7 @@ trait B {}
 impl B for T {}
 impl A for T {}
 impl A for &str {}
+//~^ ERROR type annotations needed: cannot satisfy `&str: A`
 impl A for (T,) {}
 trait TraitWithAssoc {
     type Assoc;
diff --git a/tests/ui/marker_trait_attr/unsound-overlap.stderr b/tests/ui/marker_trait_attr/unsound-overlap.stderr
index 5e58f5227ed6..13498fa4b220 100644
--- a/tests/ui/marker_trait_attr/unsound-overlap.stderr
+++ b/tests/ui/marker_trait_attr/unsound-overlap.stderr
@@ -1,5 +1,19 @@
+error[E0283]: type annotations needed: cannot satisfy `&str: A`
+  --> $DIR/unsound-overlap.rs:10:12
+   |
+LL | impl A for &str {}
+   |            ^^^^
+   |
+note: multiple `impl`s satisfying `&str: A` found
+  --> $DIR/unsound-overlap.rs:9:1
+   |
+LL | impl A for T {}
+   | ^^^^^^^^^^^^^^^^^^
+LL | impl A for &str {}
+   | ^^^^^^^^^^^^^^^
+
 error[E0119]: conflicting implementations of trait `TraitWithAssoc` for type `((&str,),)`
-  --> $DIR/unsound-overlap.rs:20:1
+  --> $DIR/unsound-overlap.rs:21:1
    |
 LL | impl TraitWithAssoc for T {
    | ------------------------------- first implementation here
@@ -7,6 +21,7 @@ LL | impl TraitWithAssoc for T {
 LL | impl TraitWithAssoc for ((&str,),) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `((&str,),)`
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0119`.
+Some errors have detailed explanations: E0119, E0283.
+For more information about an error, try `rustc --explain E0119`.
diff --git a/tests/ui/specialization/issue-39448.rs b/tests/ui/specialization/issue-39448.rs
index a15c4bd6b7ff..1c8843d983a4 100644
--- a/tests/ui/specialization/issue-39448.rs
+++ b/tests/ui/specialization/issue-39448.rs
@@ -22,6 +22,7 @@ trait FromA {
 }
 
 impl> FromA for U {
+    //~^ ERROR cycle detected when computing whether impls specialize one another
     default fn from(x: T) -> Self {
         ToA::to(x)
     }
@@ -42,7 +43,7 @@ where
 
 #[allow(dead_code)]
 fn foo(x: T, y: U) -> U {
-    x.foo(y.to()).to() //~ ERROR overflow evaluating the requirement
+    x.foo(y.to()).to()
 }
 
 fn main() {
diff --git a/tests/ui/specialization/issue-39448.stderr b/tests/ui/specialization/issue-39448.stderr
index dc5db4f4285c..e2c5f8c48468 100644
--- a/tests/ui/specialization/issue-39448.stderr
+++ b/tests/ui/specialization/issue-39448.stderr
@@ -8,28 +8,21 @@ LL | #![feature(specialization)]
    = help: consider using `min_specialization` instead, which is more stable and complete
    = note: `#[warn(incomplete_features)]` on by default
 
-error[E0275]: overflow evaluating the requirement `T: FromA`
-  --> $DIR/issue-39448.rs:45:13
-   |
-LL |     x.foo(y.to()).to()
-   |             ^^
-   |
-note: required for `T` to implement `FromA`
-  --> $DIR/issue-39448.rs:24:29
+error[E0391]: cycle detected when computing whether impls specialize one another
+  --> $DIR/issue-39448.rs:24:1
    |
 LL | impl> FromA for U {
-   |                   --------  ^^^^^^^^     ^
-   |                   |
-   |                   unsatisfied trait bound introduced here
-note: required for `U` to implement `ToA`
-  --> $DIR/issue-39448.rs:34:12
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL | impl ToA for T
-   |            ^^^^^^     ^
-LL | where
-LL |     U: FromA,
-   |        -------- unsatisfied trait bound introduced here
+   = note: ...which requires evaluating trait selection obligation `u16: FromA`...
+   = note: ...which again requires computing whether impls specialize one another, completing the cycle
+note: cycle used when building specialization graph of trait `FromA`
+  --> $DIR/issue-39448.rs:20:1
+   |
+LL | trait FromA {
+   | ^^^^^^^^^^^^^^
+   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: aborting due to 1 previous error; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0275`.
+For more information about this error, try `rustc --explain E0391`.
diff --git a/tests/ui/specialization/issue-39618.rs b/tests/ui/specialization/issue-39618.rs
index 5b9b012e6653..14a6fcf572ea 100644
--- a/tests/ui/specialization/issue-39618.rs
+++ b/tests/ui/specialization/issue-39618.rs
@@ -2,8 +2,6 @@
 // FIXME(JohnTitor): Centril pointed out this looks suspicions, we should revisit here.
 // More context: https://github.com/rust-lang/rust/pull/69192#discussion_r379846796
 
-//@ check-pass
-
 #![feature(specialization)] //~ WARN the feature `specialization` is incomplete
 
 trait Foo {
@@ -19,6 +17,7 @@ impl Bar for T where T: Foo {
 }
 
 impl Foo for T where T: Bar {
+    //~^ ERROR cycle detected when computing whether impls specialize one another
     fn foo(&self) {}
 }
 
diff --git a/tests/ui/specialization/issue-39618.stderr b/tests/ui/specialization/issue-39618.stderr
index 19de60c7c178..756162ce92c3 100644
--- a/tests/ui/specialization/issue-39618.stderr
+++ b/tests/ui/specialization/issue-39618.stderr
@@ -1,5 +1,5 @@
 warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-39618.rs:7:12
+  --> $DIR/issue-39618.rs:5:12
    |
 LL | #![feature(specialization)]
    |            ^^^^^^^^^^^^^^
@@ -8,5 +8,21 @@ LL | #![feature(specialization)]
    = help: consider using `min_specialization` instead, which is more stable and complete
    = note: `#[warn(incomplete_features)]` on by default
 
-warning: 1 warning emitted
+error[E0391]: cycle detected when computing whether impls specialize one another
+  --> $DIR/issue-39618.rs:19:1
+   |
+LL | impl Foo for T where T: Bar {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ...which requires evaluating trait selection obligation `u64: Bar`...
+   = note: ...which again requires computing whether impls specialize one another, completing the cycle
+note: cycle used when building specialization graph of trait `Foo`
+  --> $DIR/issue-39618.rs:7:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
+   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
+error: aborting due to 1 previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0391`.

From 88296bddf87d927228c5c8ff4d3ad6fa59ff9362 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Wed, 20 Mar 2024 15:47:00 -0400
Subject: [PATCH 149/192] Remove EvaluatedToErrStackDependent

---
 compiler/rustc_middle/src/traits/select.rs    | 52 ++-----------------
 compiler/rustc_trait_selection/src/infer.rs   |  3 +-
 .../src/traits/coherence.rs                   |  2 +-
 .../src/traits/select/mod.rs                  | 42 ++-------------
 4 files changed, 9 insertions(+), 90 deletions(-)

diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index 8e9751f45294..c35524373c7a 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -193,7 +193,6 @@ pub enum SelectionCandidate<'tcx> {
 /// The evaluation results are ordered:
 ///     - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions`
 ///       implies `EvaluatedToAmbig` implies `EvaluatedToAmbigStackDependent`
-///     - `EvaluatedToErr` implies `EvaluatedToErrStackDependent`
 ///     - the "union" of evaluation results is equal to their maximum -
 ///     all the "potential success" candidates can potentially succeed,
 ///     so they are noops when unioned with a definite error, and within
@@ -219,52 +218,9 @@ pub enum EvaluationResult {
     /// variables. We are somewhat imprecise there, so we don't actually
     /// know the real result.
     ///
-    /// This can't be trivially cached for the same reason as `EvaluatedToErrStackDependent`.
+    /// This can't be trivially cached because the result depends on the
+    /// stack results.
     EvaluatedToAmbigStackDependent,
-    /// Evaluation failed because we encountered an obligation we are already
-    /// trying to prove on this branch.
-    ///
-    /// We know this branch can't be a part of a minimal proof-tree for
-    /// the "root" of our cycle, because then we could cut out the recursion
-    /// and maintain a valid proof tree. However, this does not mean
-    /// that all the obligations on this branch do not hold -- it's possible
-    /// that we entered this branch "speculatively", and that there
-    /// might be some other way to prove this obligation that does not
-    /// go through this cycle -- so we can't cache this as a failure.
-    ///
-    /// For example, suppose we have this:
-    ///
-    /// ```rust,ignore (pseudo-Rust)
-    /// pub trait Trait { fn xyz(); }
-    /// // This impl is "useless", but we can still have
-    /// // an `impl Trait for SomeUnsizedType` somewhere.
-    /// impl Trait for T { fn xyz() {} }
-    ///
-    /// pub fn foo() {
-    ///     ::xyz();
-    /// }
-    /// ```
-    ///
-    /// When checking `foo`, we have to prove `T: Trait`. This basically
-    /// translates into this:
-    ///
-    /// ```plain,ignore
-    /// (T: Trait + Sized →_\impl T: Trait), T: Trait ⊢ T: Trait
-    /// ```
-    ///
-    /// When we try to prove it, we first go the first option, which
-    /// recurses. This shows us that the impl is "useless" -- it won't
-    /// tell us that `T: Trait` unless it already implemented `Trait`
-    /// by some other means. However, that does not prevent `T: Trait`
-    /// does not hold, because of the bound (which can indeed be satisfied
-    /// by `SomeUnsizedType` from another crate).
-    //
-    // FIXME: when an `EvaluatedToErrStackDependent` goes past its parent root, we
-    // ought to convert it to an `EvaluatedToErr`, because we know
-    // there definitely isn't a proof tree for that obligation. Not
-    // doing so is still sound -- there isn't any proof tree, so the
-    // branch still can't be a part of a minimal one -- but does not re-enable caching.
-    EvaluatedToErrStackDependent,
     /// Evaluation failed.
     EvaluatedToErr,
 }
@@ -290,13 +246,13 @@ impl EvaluationResult {
             | EvaluatedToAmbig
             | EvaluatedToAmbigStackDependent => true,
 
-            EvaluatedToErr | EvaluatedToErrStackDependent => false,
+            EvaluatedToErr => false,
         }
     }
 
     pub fn is_stack_dependent(self) -> bool {
         match self {
-            EvaluatedToAmbigStackDependent | EvaluatedToErrStackDependent => true,
+            EvaluatedToAmbigStackDependent => true,
 
             EvaluatedToOkModuloOpaqueTypes
             | EvaluatedToOk
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index f694dd007036..7056288e758c 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -49,8 +49,7 @@ impl<'tcx> InferCtxt<'tcx> {
     /// - the parameter environment
     ///
     /// Invokes `evaluate_obligation`, so in the event that evaluating
-    /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent
-    /// (or EvaluatedToAmbigStackDependent) will be returned.
+    /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned.
     #[instrument(level = "debug", skip(self, params), ret)]
     fn type_implements_trait(
         &self,
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 2712ba194515..6e768b23ef8c 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -210,7 +210,7 @@ fn overlap<'tcx>(
         .intercrate(true)
         .with_next_trait_solver(tcx.next_trait_solver_in_coherence())
         .build();
-    let selcx = &mut SelectionContext::with_treat_inductive_cycle_as_ambig(&infcx);
+    let selcx = &mut SelectionContext::new(&infcx);
     if track_ambiguity_causes.is_yes() {
         selcx.enable_tracking_intercrate_ambiguity_causes();
     }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 81b665236889..495ca25f6966 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -126,8 +126,6 @@ pub struct SelectionContext<'cx, 'tcx> {
     /// policy. In essence, canonicalized queries need their errors propagated
     /// rather than immediately reported because we do not have accurate spans.
     query_mode: TraitQueryMode,
-
-    treat_inductive_cycle: TreatInductiveCycleAs,
 }
 
 // A stack that walks back up the stack frame.
@@ -208,27 +206,6 @@ enum BuiltinImplConditions<'tcx> {
     Ambiguous,
 }
 
-#[derive(Copy, Clone)]
-pub enum TreatInductiveCycleAs {
-    /// This is the previous behavior, where `Recur` represents an inductive
-    /// cycle that is known not to hold. This is not forwards-compatible with
-    /// coinduction, and will be deprecated. This is the default behavior
-    /// of the old trait solver due to back-compat reasons.
-    Recur,
-    /// This is the behavior of the new trait solver, where inductive cycles
-    /// are treated as ambiguous and possibly holding.
-    Ambig,
-}
-
-impl From for EvaluationResult {
-    fn from(treat: TreatInductiveCycleAs) -> EvaluationResult {
-        match treat {
-            TreatInductiveCycleAs::Ambig => EvaluatedToAmbigStackDependent,
-            TreatInductiveCycleAs::Recur => EvaluatedToErrStackDependent,
-        }
-    }
-}
-
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> {
         SelectionContext {
@@ -236,19 +213,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             freshener: infcx.freshener(),
             intercrate_ambiguity_causes: None,
             query_mode: TraitQueryMode::Standard,
-            treat_inductive_cycle: TreatInductiveCycleAs::Ambig,
-        }
-    }
-
-    pub fn with_treat_inductive_cycle_as_ambig(
-        infcx: &'cx InferCtxt<'tcx>,
-    ) -> SelectionContext<'cx, 'tcx> {
-        // Should be executed in a context where caching is disabled,
-        // otherwise the cache is poisoned with the temporary result.
-        assert!(infcx.intercrate);
-        SelectionContext {
-            treat_inductive_cycle: TreatInductiveCycleAs::Ambig,
-            ..SelectionContext::new(infcx)
         }
     }
 
@@ -756,7 +720,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                 stack.update_reached_depth(stack_arg.1);
                                 return Ok(EvaluatedToOk);
                             } else {
-                                return Ok(self.treat_inductive_cycle.into());
+                                return Ok(EvaluatedToAmbigStackDependent);
                             }
                         }
                         return Ok(EvaluatedToOk);
@@ -875,7 +839,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             }
                         }
                         ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig),
-                        ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()),
+                        ProjectAndUnifyResult::Recursive => Ok(EvaluatedToAmbigStackDependent),
                         ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr),
                     }
                 }
@@ -1180,7 +1144,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 Some(EvaluatedToOk)
             } else {
                 debug!("evaluate_stack --> recursive, inductive");
-                Some(self.treat_inductive_cycle.into())
+                Some(EvaluatedToAmbigStackDependent)
             }
         } else {
             None

From 56dbeeb5ac05d8e34983db20453d72bdeb222cbe Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Sun, 31 Mar 2024 21:03:59 -0400
Subject: [PATCH 150/192] Add regression tests for 123303

---
 .../traits/stack-error-order-dependence-2.rs  | 24 +++++++++++++++++++
 .../ui/traits/stack-error-order-dependence.rs | 19 +++++++++++++++
 2 files changed, 43 insertions(+)
 create mode 100644 tests/ui/traits/stack-error-order-dependence-2.rs
 create mode 100644 tests/ui/traits/stack-error-order-dependence.rs

diff --git a/tests/ui/traits/stack-error-order-dependence-2.rs b/tests/ui/traits/stack-error-order-dependence-2.rs
new file mode 100644
index 000000000000..323685aa15bb
--- /dev/null
+++ b/tests/ui/traits/stack-error-order-dependence-2.rs
@@ -0,0 +1,24 @@
+//@ check-pass
+// Regression test for .
+// This time EXCEPT without `dyn` builtin bounds :^)
+
+pub trait Trait: Supertrait {}
+
+trait Impossible {}
+impl Trait for F {}
+
+pub trait Supertrait {}
+
+impl Supertrait for T {}
+
+fn needs_supertrait() {}
+fn needs_trait() {}
+
+struct A;
+impl Trait for A where A: Supertrait {}
+impl Supertrait for A {}
+
+fn main() {
+    needs_supertrait::();
+    needs_trait::();
+}
diff --git a/tests/ui/traits/stack-error-order-dependence.rs b/tests/ui/traits/stack-error-order-dependence.rs
new file mode 100644
index 000000000000..037c292a542d
--- /dev/null
+++ b/tests/ui/traits/stack-error-order-dependence.rs
@@ -0,0 +1,19 @@
+//@ check-pass
+// Regression test for .
+
+pub trait Trait: Supertrait {}
+
+trait Impossible {}
+impl Trait for F {}
+
+pub trait Supertrait {}
+
+impl Supertrait for T {}
+
+fn needs_supertrait() {}
+fn needs_trait() {}
+
+fn main() {
+    needs_supertrait::();
+    needs_trait::();
+}

From 6e5f1dacf3a670cf03111039a032c765355839aa Mon Sep 17 00:00:00 2001
From: beetrees 
Date: Sun, 24 Mar 2024 01:03:39 +0000
Subject: [PATCH 151/192] Use the `Align` type when parsing alignment
 attributes

---
 Cargo.lock                                    |  1 +
 compiler/rustc_abi/src/lib.rs                 |  7 ++--
 compiler/rustc_attr/Cargo.toml                |  1 +
 compiler/rustc_attr/src/builtin.rs            | 19 ++++++---
 compiler/rustc_codegen_llvm/src/attributes.rs |  2 +-
 .../src/coverageinfo/mod.rs                   |  7 ++--
 compiler/rustc_codegen_llvm/src/llvm/mod.rs   |  5 ++-
 .../rustc_hir_analysis/src/check/check.rs     |  2 +-
 .../src/middle/codegen_fn_attrs.rs            |  3 +-
 compiler/rustc_middle/src/ty/mod.rs           |  3 +-
 tests/ui/repr/repr-align.rs                   |  8 ++++
 tests/ui/repr/repr-align.stderr               | 42 +++++++++++++++----
 12 files changed, 74 insertions(+), 26 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index b8fe1ebaf801..2b601df344b3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3560,6 +3560,7 @@ dependencies = [
 name = "rustc_attr"
 version = "0.0.0"
 dependencies = [
+ "rustc_abi",
  "rustc_ast",
  "rustc_ast_pretty",
  "rustc_data_structures",
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 4f2b9d0ef50d..7439af7aed3e 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -698,6 +698,7 @@ impl fmt::Display for AlignFromBytesError {
 
 impl Align {
     pub const ONE: Align = Align { pow2: 0 };
+    pub const EIGHT: Align = Align { pow2: 3 };
     // LLVM has a maximal supported alignment of 2^29, we inherit that.
     pub const MAX: Align = Align { pow2: 29 };
 
@@ -707,19 +708,19 @@ impl Align {
     }
 
     #[inline]
-    pub fn from_bytes(align: u64) -> Result {
+    pub const fn from_bytes(align: u64) -> Result {
         // Treat an alignment of 0 bytes like 1-byte alignment.
         if align == 0 {
             return Ok(Align::ONE);
         }
 
         #[cold]
-        fn not_power_of_2(align: u64) -> AlignFromBytesError {
+        const fn not_power_of_2(align: u64) -> AlignFromBytesError {
             AlignFromBytesError::NotPowerOfTwo(align)
         }
 
         #[cold]
-        fn too_large(align: u64) -> AlignFromBytesError {
+        const fn too_large(align: u64) -> AlignFromBytesError {
             AlignFromBytesError::TooLarge(align)
         }
 
diff --git a/compiler/rustc_attr/Cargo.toml b/compiler/rustc_attr/Cargo.toml
index d33416d20038..3b24452450ab 100644
--- a/compiler/rustc_attr/Cargo.toml
+++ b/compiler/rustc_attr/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
+rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 814104ec78c4..439f13e76357 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -1,5 +1,6 @@
 //! Parsing and validation of builtin attributes
 
+use rustc_abi::Align;
 use rustc_ast::{self as ast, attr};
 use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem, NodeId};
 use rustc_ast_pretty::pprust;
@@ -919,10 +920,10 @@ pub enum ReprAttr {
     ReprInt(IntType),
     ReprRust,
     ReprC,
-    ReprPacked(u32),
+    ReprPacked(Align),
     ReprSimd,
     ReprTransparent,
-    ReprAlign(u32),
+    ReprAlign(Align),
 }
 
 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
@@ -968,7 +969,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec {
                 let hint = match item.name_or_empty() {
                     sym::Rust => Some(ReprRust),
                     sym::C => Some(ReprC),
-                    sym::packed => Some(ReprPacked(1)),
+                    sym::packed => Some(ReprPacked(Align::ONE)),
                     sym::simd => Some(ReprSimd),
                     sym::transparent => Some(ReprTransparent),
                     sym::align => {
@@ -1209,11 +1210,17 @@ fn allow_unstable<'a>(
     })
 }
 
-pub fn parse_alignment(node: &ast::LitKind) -> Result {
+pub fn parse_alignment(node: &ast::LitKind) -> Result {
     if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
+        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
         if literal.get().is_power_of_two() {
-            // rustc_middle::ty::layout::Align restricts align to <= 2^29
-            if *literal <= 1 << 29 { Ok(literal.get() as u32) } else { Err("larger than 2^29") }
+            // Only possible error is larger than 2^29
+            literal
+                .get()
+                .try_into()
+                .ok()
+                .and_then(|v| Align::from_bytes(v).ok())
+                .ok_or("larger than 2^29")
         } else {
             Err("not a power of two")
         }
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index f9eaa0d94cbb..870e5ab32961 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -417,7 +417,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
         to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry"));
     }
     if let Some(align) = codegen_fn_attrs.alignment {
-        llvm::set_alignment(llfn, align as usize);
+        llvm::set_alignment(llfn, align);
     }
     to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
 
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 54f4bc063402..07aac92dacbb 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -17,6 +17,7 @@ use rustc_middle::mir::coverage::CoverageKind;
 use rustc_middle::mir::Coverage;
 use rustc_middle::ty::layout::HasTyCtxt;
 use rustc_middle::ty::Instance;
+use rustc_target::abi::Align;
 
 use std::cell::RefCell;
 
@@ -24,7 +25,7 @@ pub(crate) mod ffi;
 pub(crate) mod map_data;
 pub mod mapgen;
 
-const VAR_ALIGN_BYTES: usize = 8;
+const VAR_ALIGN: Align = Align::EIGHT;
 
 /// A context object for maintaining all state needed by the coverageinfo module.
 pub struct CrateCoverageContext<'ll, 'tcx> {
@@ -227,7 +228,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>(
     llvm::set_global_constant(llglobal, true);
     llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
     llvm::set_section(llglobal, &covmap_section_name);
-    llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
+    llvm::set_alignment(llglobal, VAR_ALIGN);
     cx.add_used_global(llglobal);
 }
 
@@ -257,7 +258,7 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
     llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
     llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
     llvm::set_section(llglobal, covfun_section_name);
-    llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
+    llvm::set_alignment(llglobal, VAR_ALIGN);
     llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
     cx.add_used_global(llglobal);
 }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
index 4f5cc575da6e..6ab1eea95976 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
@@ -11,6 +11,7 @@ pub use self::RealPredicate::*;
 use libc::c_uint;
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_llvm::RustString;
+use rustc_target::abi::Align;
 use std::cell::RefCell;
 use std::ffi::{CStr, CString};
 use std::str::FromStr;
@@ -229,9 +230,9 @@ pub fn set_visibility(llglobal: &Value, visibility: Visibility) {
     }
 }
 
-pub fn set_alignment(llglobal: &Value, bytes: usize) {
+pub fn set_alignment(llglobal: &Value, align: Align) {
     unsafe {
-        ffi::LLVMSetAlignment(llglobal, bytes as c_uint);
+        ffi::LLVMSetAlignment(llglobal, align.bytes() as c_uint);
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 1286a724e957..a880445a27c1 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -964,7 +964,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
             for r in attr::parse_repr_attr(tcx.sess, attr) {
                 if let attr::ReprPacked(pack) = r
                     && let Some(repr_pack) = repr.pack
-                    && pack as u64 != repr_pack.bytes()
+                    && pack != repr_pack
                 {
                     struct_span_code_err!(
                         tcx.dcx(),
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 7d6d39a2a8a3..ec9697bbd355 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -1,6 +1,7 @@
 use crate::mir::mono::Linkage;
 use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr};
 use rustc_span::symbol::Symbol;
+use rustc_target::abi::Align;
 use rustc_target::spec::SanitizerSet;
 
 #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
@@ -42,7 +43,7 @@ pub struct CodegenFnAttrs {
     pub instruction_set: Option,
     /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be
     /// aligned to.
-    pub alignment: Option,
+    pub alignment: Option,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 6ce53ccc8cd7..6c94ab37eb1d 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1523,7 +1523,6 @@ impl<'tcx> TyCtxt<'tcx> {
                     attr::ReprRust => ReprFlags::empty(),
                     attr::ReprC => ReprFlags::IS_C,
                     attr::ReprPacked(pack) => {
-                        let pack = Align::from_bytes(pack as u64).unwrap();
                         min_pack = Some(if let Some(min_pack) = min_pack {
                             min_pack.min(pack)
                         } else {
@@ -1555,7 +1554,7 @@ impl<'tcx> TyCtxt<'tcx> {
                         ReprFlags::empty()
                     }
                     attr::ReprAlign(align) => {
-                        max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap()));
+                        max_align = max_align.max(Some(align));
                         ReprFlags::empty()
                     }
                 });
diff --git a/tests/ui/repr/repr-align.rs b/tests/ui/repr/repr-align.rs
index 58ecf9a51832..33aa727d4bd0 100644
--- a/tests/ui/repr/repr-align.rs
+++ b/tests/ui/repr/repr-align.rs
@@ -15,6 +15,10 @@ struct S2(i32);
 #[repr(align(536870912))] // ok: this is the largest accepted alignment
 struct S3(i32);
 
+#[repr(align(0))] //~ ERROR: invalid `repr(align)` attribute: not a power of two
+                  //~| ERROR: invalid `repr(align)` attribute: not a power of two
+struct S4(i32);
+
 #[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
                      //~| ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
 enum E0 { A, B }
@@ -30,4 +34,8 @@ enum E2 { A, B }
 #[repr(align(536870912))] // ok: this is the largest accepted alignment
 enum E3 { A, B }
 
+#[repr(align(0))] //~ ERROR: invalid `repr(align)` attribute: not a power of two
+                  //~| ERROR: invalid `repr(align)` attribute: not a power of two
+enum E4 { A, B }
+
 fn main() {}
diff --git a/tests/ui/repr/repr-align.stderr b/tests/ui/repr/repr-align.stderr
index bb0e17ba3955..660247840c48 100644
--- a/tests/ui/repr/repr-align.stderr
+++ b/tests/ui/repr/repr-align.stderr
@@ -16,24 +16,36 @@ error[E0589]: invalid `repr(align)` attribute: larger than 2^29
 LL | #[repr(align(4294967296))]
    |              ^^^^^^^^^^
 
-error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
+error[E0589]: invalid `repr(align)` attribute: not a power of two
   --> $DIR/repr-align.rs:18:14
    |
+LL | #[repr(align(0))]
+   |              ^
+
+error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
+  --> $DIR/repr-align.rs:22:14
+   |
 LL | #[repr(align(16.0))]
    |              ^^^^
 
 error[E0589]: invalid `repr(align)` attribute: not a power of two
-  --> $DIR/repr-align.rs:22:14
+  --> $DIR/repr-align.rs:26:14
    |
 LL | #[repr(align(15))]
    |              ^^
 
 error[E0589]: invalid `repr(align)` attribute: larger than 2^29
-  --> $DIR/repr-align.rs:26:14
+  --> $DIR/repr-align.rs:30:14
    |
 LL | #[repr(align(4294967296))]
    |              ^^^^^^^^^^
 
+error[E0589]: invalid `repr(align)` attribute: not a power of two
+  --> $DIR/repr-align.rs:37:14
+   |
+LL | #[repr(align(0))]
+   |              ^
+
 error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
   --> $DIR/repr-align.rs:3:14
    |
@@ -58,16 +70,24 @@ LL | #[repr(align(4294967296))]
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
+error[E0589]: invalid `repr(align)` attribute: not a power of two
   --> $DIR/repr-align.rs:18:14
    |
+LL | #[repr(align(0))]
+   |              ^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
+  --> $DIR/repr-align.rs:22:14
+   |
 LL | #[repr(align(16.0))]
    |              ^^^^
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0589]: invalid `repr(align)` attribute: not a power of two
-  --> $DIR/repr-align.rs:22:14
+  --> $DIR/repr-align.rs:26:14
    |
 LL | #[repr(align(15))]
    |              ^^
@@ -75,13 +95,21 @@ LL | #[repr(align(15))]
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0589]: invalid `repr(align)` attribute: larger than 2^29
-  --> $DIR/repr-align.rs:26:14
+  --> $DIR/repr-align.rs:30:14
    |
 LL | #[repr(align(4294967296))]
    |              ^^^^^^^^^^
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error: aborting due to 12 previous errors
+error[E0589]: invalid `repr(align)` attribute: not a power of two
+  --> $DIR/repr-align.rs:37:14
+   |
+LL | #[repr(align(0))]
+   |              ^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 16 previous errors
 
 For more information about this error, try `rustc --explain E0589`.

From 4ff8a9bd6b64e32703603cf8bc8cb5cb221d4889 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Sun, 31 Mar 2024 22:33:36 -0400
Subject: [PATCH 152/192] Don't inherit codegen attrs from parent static

---
 compiler/rustc_const_eval/src/interpret/intern.rs     | 11 ++++++++---
 .../nested-allocations-dont-inherit-codegen-attrs.rs  | 11 +++++++++++
 2 files changed, 19 insertions(+), 3 deletions(-)
 create mode 100644 tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs

diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 58eaef65e557..f4e46c9499e2 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -18,6 +18,7 @@ use rustc_ast::Mutability;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult};
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::layout::TyAndLayout;
@@ -106,13 +107,17 @@ fn intern_as_new_static<'tcx>(
         DefKind::Static { mutability: alloc.0.mutability, nested: true },
     );
     tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
-    feed.codegen_fn_attrs(tcx.codegen_fn_attrs(static_id).clone());
+
+    // These do not inherit the codegen attrs of the parent static allocation, since
+    // it doesn't make sense for them to inherit their `#[no_mangle]` and `#[link_name = ..]`
+    // and the like.
+    feed.codegen_fn_attrs(CodegenFnAttrs::new());
+
     feed.eval_static_initializer(Ok(alloc));
     feed.generics_of(tcx.generics_of(static_id).clone());
     feed.def_ident_span(tcx.def_ident_span(static_id));
     feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id));
-
-    feed.feed_hir()
+    feed.feed_hir();
 }
 
 /// How a constant value should be interned.
diff --git a/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs b/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs
new file mode 100644
index 000000000000..0b7e659c7b75
--- /dev/null
+++ b/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs
@@ -0,0 +1,11 @@
+//@ build-pass
+
+// Make sure that the nested static allocation for `FOO` doesn't inherit `no_mangle`.
+#[no_mangle]
+pub static mut FOO: &mut [i32] = &mut [42];
+
+// Make sure that the nested static allocation for `BAR` doesn't inherit `export_name`.
+#[export_name = "BAR_"]
+pub static mut BAR: &mut [i32] = &mut [42];
+
+fn main() {}

From 0bbaa2505be6d7e6483a0dcbe585c96684e73da5 Mon Sep 17 00:00:00 2001
From: beetrees 
Date: Sun, 17 Mar 2024 21:12:17 +0000
Subject: [PATCH 153/192] Fix error message for `env!` when env var is not
 valid Unicode

---
 compiler/rustc_builtin_macros/messages.ftl    |  2 +
 compiler/rustc_builtin_macros/src/env.rs      | 51 +++++++++++--------
 compiler/rustc_builtin_macros/src/errors.rs   |  8 +++
 library/core/src/macros/mod.rs                |  3 +-
 src/tools/run-make-support/src/lib.rs         |  6 ++-
 src/tools/run-make-support/src/rustc.rs       | 18 +++++++
 .../non-unicode-env/non_unicode_env.rs        |  3 ++
 .../non-unicode-env/non_unicode_env.stderr    | 10 ++++
 tests/run-make/non-unicode-env/rmake.rs       | 14 +++++
 9 files changed, 92 insertions(+), 23 deletions(-)
 create mode 100644 tests/run-make/non-unicode-env/non_unicode_env.rs
 create mode 100644 tests/run-make/non-unicode-env/non_unicode_env.stderr
 create mode 100644 tests/run-make/non-unicode-env/rmake.rs

diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index ba8401393d7a..2a5bc58af3b3 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -114,6 +114,8 @@ builtin_macros_env_not_defined = environment variable `{$var}` not defined at co
     .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
     .custom = use `std::env::var({$var_expr})` to read the variable at run time
 
+builtin_macros_env_not_unicode = environment variable `{$var}` is not a valid Unicode string
+
 builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments
 
 builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern
diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs
index bce710e5cab1..938730459437 100644
--- a/compiler/rustc_builtin_macros/src/env.rs
+++ b/compiler/rustc_builtin_macros/src/env.rs
@@ -11,18 +11,19 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 use std::env;
+use std::env::VarError;
 use thin_vec::thin_vec;
 
 use crate::errors;
 
-fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Option {
+fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result {
     let var = var.as_str();
     if let Some(value) = cx.sess.opts.logical_env.get(var) {
-        return Some(Symbol::intern(value));
+        return Ok(Symbol::intern(value));
     }
     // If the environment variable was not defined with the `--env-set` option, we try to retrieve it
     // from rustc's environment.
-    env::var(var).ok().as_deref().map(Symbol::intern)
+    Ok(Symbol::intern(&env::var(var)?))
 }
 
 pub fn expand_option_env<'cx>(
@@ -39,7 +40,7 @@ pub fn expand_option_env<'cx>(
     };
 
     let sp = cx.with_def_site_ctxt(sp);
-    let value = lookup_env(cx, var);
+    let value = lookup_env(cx, var).ok();
     cx.sess.psess.env_depinfo.borrow_mut().insert((var, value));
     let e = match value {
         None => {
@@ -108,9 +109,9 @@ pub fn expand_env<'cx>(
 
     let span = cx.with_def_site_ctxt(sp);
     let value = lookup_env(cx, var);
-    cx.sess.psess.env_depinfo.borrow_mut().insert((var, value));
+    cx.sess.psess.env_depinfo.borrow_mut().insert((var, value.as_ref().ok().copied()));
     let e = match value {
-        None => {
+        Err(err) => {
             let ExprKind::Lit(token::Lit {
                 kind: LitKind::Str | LitKind::StrRaw(..), symbol, ..
             }) = &var_expr.kind
@@ -118,25 +119,33 @@ pub fn expand_env<'cx>(
                 unreachable!("`expr_to_string` ensures this is a string lit")
             };
 
-            let guar = if let Some(msg_from_user) = custom_msg {
-                cx.dcx().emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user })
-            } else if is_cargo_env_var(var.as_str()) {
-                cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar {
-                    span,
-                    var: *symbol,
-                    var_expr: var_expr.ast_deref(),
-                })
-            } else {
-                cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar {
-                    span,
-                    var: *symbol,
-                    var_expr: var_expr.ast_deref(),
-                })
+            let guar = match err {
+                VarError::NotPresent => {
+                    if let Some(msg_from_user) = custom_msg {
+                        cx.dcx()
+                            .emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user })
+                    } else if is_cargo_env_var(var.as_str()) {
+                        cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar {
+                            span,
+                            var: *symbol,
+                            var_expr: var_expr.ast_deref(),
+                        })
+                    } else {
+                        cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar {
+                            span,
+                            var: *symbol,
+                            var_expr: var_expr.ast_deref(),
+                        })
+                    }
+                }
+                VarError::NotUnicode(_) => {
+                    cx.dcx().emit_err(errors::EnvNotUnicode { span, var: *symbol })
+                }
             };
 
             return ExpandResult::Ready(DummyResult::any(sp, guar));
         }
-        Some(value) => cx.expr_str(span, value),
+        Ok(value) => cx.expr_str(span, value),
     };
     ExpandResult::Ready(MacEager::expr(e))
 }
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 377aff8fb6c0..6b6647ef085c 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -458,6 +458,14 @@ pub(crate) enum EnvNotDefined<'a> {
     },
 }
 
+#[derive(Diagnostic)]
+#[diag(builtin_macros_env_not_unicode)]
+pub(crate) struct EnvNotUnicode {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) var: Symbol,
+}
+
 #[derive(Diagnostic)]
 #[diag(builtin_macros_format_requires_string)]
 pub(crate) struct FormatRequiresString {
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 574a357b44ab..6da05a1ca867 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -1053,7 +1053,8 @@ pub(crate) mod builtin {
     ///
     /// If the environment variable is not defined, then a compilation error
     /// will be emitted. To not emit a compile error, use the [`option_env!`]
-    /// macro instead.
+    /// macro instead. A compilation error will also be emitted if the
+    /// environment variable is not a vaild Unicode string.
     ///
     /// # Examples
     ///
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 7975677286d3..48fa2bbf1ac4 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -19,7 +19,11 @@ pub fn tmp_dir() -> PathBuf {
 }
 
 fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! {
-    eprintln!("command failed at line {caller_line_number}");
+    if output.status.success() {
+        eprintln!("command incorrectly succeeded at line {caller_line_number}");
+    } else {
+        eprintln!("command failed at line {caller_line_number}");
+    }
     eprintln!("{cmd}");
     eprintln!("output status: `{}`", output.status);
     eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap());
diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs
index 1b358817a795..50ff0d26bbb8 100644
--- a/src/tools/run-make-support/src/rustc.rs
+++ b/src/tools/run-make-support/src/rustc.rs
@@ -1,4 +1,5 @@
 use std::env;
+use std::ffi::OsStr;
 use std::path::Path;
 use std::process::{Command, Output};
 
@@ -133,6 +134,11 @@ impl Rustc {
         self
     }
 
+    pub fn env(&mut self, name: impl AsRef, value: impl AsRef) -> &mut Self {
+        self.cmd.env(name, value);
+        self
+    }
+
     // Command inspection, output and running helper methods
 
     /// Get the [`Output`][std::process::Output] of the finished `rustc` process.
@@ -153,6 +159,18 @@ impl Rustc {
         output
     }
 
+    #[track_caller]
+    pub fn run_fail(&mut self) -> Output {
+        let caller_location = std::panic::Location::caller();
+        let caller_line_number = caller_location.line();
+
+        let output = self.cmd.output().unwrap();
+        if output.status.success() {
+            handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
+        }
+        output
+    }
+
     /// Inspect what the underlying [`Command`] is up to the current construction.
     pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self {
         f(&self.cmd);
diff --git a/tests/run-make/non-unicode-env/non_unicode_env.rs b/tests/run-make/non-unicode-env/non_unicode_env.rs
new file mode 100644
index 000000000000..865fc9373657
--- /dev/null
+++ b/tests/run-make/non-unicode-env/non_unicode_env.rs
@@ -0,0 +1,3 @@
+fn main() {
+    let _ = env!("NON_UNICODE_VAR");
+}
diff --git a/tests/run-make/non-unicode-env/non_unicode_env.stderr b/tests/run-make/non-unicode-env/non_unicode_env.stderr
new file mode 100644
index 000000000000..c4dcd7b2eb70
--- /dev/null
+++ b/tests/run-make/non-unicode-env/non_unicode_env.stderr
@@ -0,0 +1,10 @@
+error: environment variable `NON_UNICODE_VAR` is not a valid Unicode string
+ --> non_unicode_env.rs:2:13
+  |
+2 |     let _ = env!("NON_UNICODE_VAR");
+  |             ^^^^^^^^^^^^^^^^^^^^^^^
+  |
+  = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 1 previous error
+
diff --git a/tests/run-make/non-unicode-env/rmake.rs b/tests/run-make/non-unicode-env/rmake.rs
new file mode 100644
index 000000000000..ba4aa1609b56
--- /dev/null
+++ b/tests/run-make/non-unicode-env/rmake.rs
@@ -0,0 +1,14 @@
+extern crate run_make_support;
+
+use run_make_support::rustc;
+
+fn main() {
+    #[cfg(unix)]
+    let non_unicode: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(&[0xFF]);
+    #[cfg(windows)]
+    let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]);
+    let output = rustc().input("non_unicode_env.rs").env("NON_UNICODE_VAR", non_unicode).run_fail();
+    let actual = std::str::from_utf8(&output.stderr).unwrap();
+    let expected = std::fs::read_to_string("non_unicode_env.stderr").unwrap();
+    assert_eq!(actual, expected);
+}

From 747d19326b3e9a87eca74947afb97cca74b843fa Mon Sep 17 00:00:00 2001
From: David Carlier 
Date: Mon, 1 Apr 2024 10:01:21 +0100
Subject: [PATCH 154/192] std::thread: adding get_name implementation for
 solaris/illumos.

THREAD_NAME_MAX is 32 (31 max + 1 for the null terminator).
---
 library/std/src/sys/pal/unix/thread.rs | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 77e31d802a38..79e4c4788c4e 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -225,13 +225,19 @@ impl Thread {
         // Newlib, Emscripten, and VxWorks have no way to set a thread name.
     }
 
-    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd",))]
+    #[cfg(any(
+        target_os = "linux",
+        target_os = "freebsd",
+        target_os = "netbsd",
+        target_os = "solaris",
+        target_os = "illumos"
+    ))]
     pub fn get_name() -> Option {
         #[cfg(target_os = "linux")]
         const TASK_COMM_LEN: usize = 16;
         #[cfg(target_os = "freebsd")]
         const TASK_COMM_LEN: usize = libc::MAXCOMLEN + 1;
-        #[cfg(target_os = "netbsd")]
+        #[cfg(any(target_os = "netbsd", target_os = "solaris", target_os = "illumos"))]
         const TASK_COMM_LEN: usize = 32;
         let mut name = vec![0u8; TASK_COMM_LEN];
         let res = unsafe {
@@ -282,7 +288,9 @@ impl Thread {
         target_os = "ios",
         target_os = "tvos",
         target_os = "watchos",
-        target_os = "haiku"
+        target_os = "haiku",
+        target_os = "solaris",
+        target_os = "illumos"
     )))]
     pub fn get_name() -> Option {
         None

From edb7aeafba0a7a93408e81e8ffeb99317e97cbba Mon Sep 17 00:00:00 2001
From: Tom French <15848336+TomAFrench@users.noreply.github.com>
Date: Mon, 1 Apr 2024 11:43:52 +0100
Subject: [PATCH 155/192] chore: fix footnotes/links in `platform-support.md`

---
 src/doc/rustc/src/platform-support.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 75d38dd20bde..aa982a445033 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -203,6 +203,7 @@ target | std | notes
 [`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI
 
 [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue].
+
 [wasi-rename]: https://github.com/rust-lang/compiler-team/issues/607
 
 [Fortanix ABI]: https://edp.fortanix.com/

From 71077482d58e807b9d09637237015df3f3d7d690 Mon Sep 17 00:00:00 2001
From: Maybe Waffle 
Date: Mon, 1 Apr 2024 10:56:33 +0000
Subject: [PATCH 156/192] Fixup parsing of `rustc_never_type_options` attribute

Copy paste error strike again..
---
 compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 74f27cfebbd2..05e7c5b2b418 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -416,13 +416,13 @@ fn parse_never_type_options_attr(
             continue;
         }
 
-        if item.has_name(sym::diverging_block_default) && fallback.is_none() {
-            let mode = item.value_str().unwrap();
-            match mode {
+        if item.has_name(sym::diverging_block_default) && block.is_none() {
+            let default = item.value_str().unwrap();
+            match default {
                 sym::unit => block = Some(DivergingBlockBehavior::Unit),
                 sym::never => block = Some(DivergingBlockBehavior::Never),
                 _ => {
-                    tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{mode}` (supported: `unit` and `never`)"));
+                    tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{default}` (supported: `unit` and `never`)"));
                 }
             };
             continue;
@@ -431,7 +431,7 @@ fn parse_never_type_options_attr(
         tcx.dcx().span_err(
             item.span(),
             format!(
-                "unknown never type option: `{}` (supported: `fallback`)",
+                "unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)",
                 item.name_or_empty()
             ),
         );

From d7b55e4c90b6723d95b7cb8c152660b2ef7afcba Mon Sep 17 00:00:00 2001
From: joboet 
Date: Mon, 1 Apr 2024 15:28:27 +0200
Subject: [PATCH 157/192] update comment

---
 library/std/src/rt.rs | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index 0bafd9f352e3..ff6e433ebce3 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -95,10 +95,7 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
     unsafe {
         sys::init(argc, argv, sigpipe);
 
-        // Next, set up the current Thread with the guard information we just
-        // created. Note that this isn't necessary in general for new threads,
-        // but we just do this to name the main thread and to give it correct
-        // info about the stack bounds.
+        // Set up the current thread to give it the right name.
         let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main"))));
         thread::set_current(thread);
     }

From a91f6221b95dd21394fae4fd1ca45e56475b355f Mon Sep 17 00:00:00 2001
From: Boxy 
Date: Mon, 1 Apr 2024 17:29:34 +0100
Subject: [PATCH 158/192] Update `ParamEnv` docs

---
 compiler/rustc_middle/src/ty/mod.rs | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 99da981b9d61..d7c6bffe284f 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1034,9 +1034,11 @@ impl PlaceholderLike for PlaceholderConst {
     }
 }
 
-/// When type checking, we use the `ParamEnv` to track
-/// details about the set of where-clauses that are in scope at this
-/// particular point.
+/// When interacting with the type system we must provide information about the
+/// environment. `ParamEnv` is the type that represents this information. See the
+/// [dev guide chapter](param_env_guide) for more information.
+///
+/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html
 #[derive(Copy, Clone, Hash, PartialEq, Eq)]
 pub struct ParamEnv<'tcx> {
     /// This packs both caller bounds and the reveal enum into one pointer.
@@ -1103,8 +1105,11 @@ impl<'tcx> TypeVisitable> for ParamEnv<'tcx> {
 impl<'tcx> ParamEnv<'tcx> {
     /// Construct a trait environment suitable for contexts where
     /// there are no where-clauses in scope. Hidden types (like `impl
-    /// Trait`) are left hidden, so this is suitable for ordinary
-    /// type-checking.
+    /// Trait`) are left hidden. In majority of cases it is incorrect
+    /// to use an empty environment. See the [dev guide section][param_env_guide]
+    /// for information on what a `ParamEnv` is and how to acquire one.
+    ///
+    /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html
     #[inline]
     pub fn empty() -> Self {
         Self::new(List::empty(), Reveal::UserFacing)

From 03dd3293550c68fcdd00b011fcaa4132084f8a03 Mon Sep 17 00:00:00 2001
From: Boxy 
Date: Mon, 1 Apr 2024 19:59:05 +0100
Subject: [PATCH 159/192] maybe

---
 compiler/rustc_middle/src/ty/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index d7c6bffe284f..331daa87598d 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1036,7 +1036,7 @@ impl PlaceholderLike for PlaceholderConst {
 
 /// When interacting with the type system we must provide information about the
 /// environment. `ParamEnv` is the type that represents this information. See the
-/// [dev guide chapter](param_env_guide) for more information.
+/// [dev guide chapter][param_env_guide] for more information.
 ///
 /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html
 #[derive(Copy, Clone, Hash, PartialEq, Eq)]

From 3896f0762715092a4f6dfc81b38ba69080192bcd Mon Sep 17 00:00:00 2001
From: Jubilee Young 
Date: Mon, 1 Apr 2024 12:44:49 -0700
Subject: [PATCH 160/192] Note impact of `-Cstrip` on backtraces

It is not always clear to people what the impact of `-Cstrip` options are.
They are a common question on sites like StackOverflow, and sometimes
people even report bugs with "no backtrace" after deliberately mangling
the symbol table. We cannot exhaustively document every permutation, but
we should warn people about common effects.
---
 src/doc/rustc/src/codegen-options/index.md | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index 4a4f1ae98e40..5667f0e02ce1 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -553,9 +553,12 @@ Supported values for this option are:
   of MSVC).
 - `debuginfo` - debuginfo sections and debuginfo symbols from the symbol table
   section are stripped at link time and are not copied to the produced binary
-  or separate files.
+  or separate files. This should leave backtraces mostly-intact but may make
+  using a debugger like gdb or lldb ineffectual.
 - `symbols` - same as `debuginfo`, but the rest of the symbol table section is
-  stripped as well if the linker supports it.
+  stripped as well if the linker supports it. On platforms which depend on the
+  binary's symbol table for backtraces, this can affect them so negatively as to
+  make the trace completely incomprehensible.
 
 ## symbol-mangling-version
 

From cbd593ed18fa455093cd198893a118c1262ff1ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= 
Date: Mon, 1 Apr 2024 07:12:22 +0200
Subject: [PATCH 161/192] =?UTF-8?q?rustdoc:=20synthetic=20impls:=20auto=20?=
 =?UTF-8?q?traits:=20Fx{Hash=E2=86=A6Index}{Map,Set}?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/traits/auto_trait.rs                  |  47 ++----
 src/librustdoc/clean/auto_trait.rs            | 157 +++++-------------
 src/librustdoc/clean/mod.rs                   |   1 -
 tests/rustdoc/synthetic_auto/complex.rs       |   4 +-
 tests/rustdoc/synthetic_auto/lifetimes.rs     |   2 +-
 tests/rustdoc/synthetic_auto/no-redundancy.rs |   5 +-
 tests/rustdoc/synthetic_auto/project.rs       |   6 +-
 7 files changed, 64 insertions(+), 158 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index c909a0b49e24..7603c9ed7a86 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -6,13 +6,13 @@ use super::*;
 use crate::errors::UnableToConstructConstantValue;
 use crate::infer::region_constraints::{Constraint, RegionConstraintData};
 use crate::traits::project::ProjectAndUnifyResult;
+
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
+use rustc_data_structures::unord::UnordSet;
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::{Region, RegionVid};
 
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-
-use std::collections::hash_map::Entry;
 use std::collections::VecDeque;
 use std::iter;
 
@@ -35,17 +35,10 @@ pub enum AutoTraitResult {
     NegativeImpl,
 }
 
-#[allow(dead_code)]
-impl AutoTraitResult {
-    fn is_auto(&self) -> bool {
-        matches!(self, AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl)
-    }
-}
-
 pub struct AutoTraitInfo<'cx> {
     pub full_user_env: ty::ParamEnv<'cx>,
     pub region_data: RegionConstraintData<'cx>,
-    pub vid_to_region: FxHashMap>,
+    pub vid_to_region: FxIndexMap>,
 }
 
 pub struct AutoTraitFinder<'tcx> {
@@ -114,7 +107,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
         }
 
         let infcx = tcx.infer_ctxt().build();
-        let mut fresh_preds = FxHashSet::default();
+        let mut fresh_preds = FxIndexSet::default();
 
         // Due to the way projections are handled by SelectionContext, we need to run
         // evaluate_predicates twice: once on the original param env, and once on the result of
@@ -239,7 +232,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
         ty: Ty<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         user_env: ty::ParamEnv<'tcx>,
-        fresh_preds: &mut FxHashSet>,
+        fresh_preds: &mut FxIndexSet>,
     ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> {
         let tcx = infcx.tcx;
 
@@ -252,7 +245,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
 
         let mut select = SelectionContext::new(infcx);
 
-        let mut already_visited = FxHashSet::default();
+        let mut already_visited = UnordSet::new();
         let mut predicates = VecDeque::new();
         predicates.push_back(ty::Binder::dummy(ty::TraitPredicate {
             trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]),
@@ -473,9 +466,9 @@ impl<'tcx> AutoTraitFinder<'tcx> {
     fn map_vid_to_region<'cx>(
         &self,
         regions: &RegionConstraintData<'cx>,
-    ) -> FxHashMap> {
-        let mut vid_map: FxHashMap, RegionDeps<'cx>> = FxHashMap::default();
-        let mut finished_map = FxHashMap::default();
+    ) -> FxIndexMap> {
+        let mut vid_map = FxIndexMap::, RegionDeps<'cx>>::default();
+        let mut finished_map = FxIndexMap::default();
 
         for (constraint, _) in ®ions.constraints {
             match constraint {
@@ -513,25 +506,22 @@ impl<'tcx> AutoTraitFinder<'tcx> {
         }
 
         while !vid_map.is_empty() {
-            #[allow(rustc::potential_query_instability)]
-            let target = *vid_map.keys().next().expect("Keys somehow empty");
-            let deps = vid_map.remove(&target).expect("Entry somehow missing");
+            let target = *vid_map.keys().next().unwrap();
+            let deps = vid_map.swap_remove(&target).unwrap();
 
             for smaller in deps.smaller.iter() {
                 for larger in deps.larger.iter() {
                     match (smaller, larger) {
                         (&RegionTarget::Region(_), &RegionTarget::Region(_)) => {
-                            if let Entry::Occupied(v) = vid_map.entry(*smaller) {
+                            if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) {
                                 let smaller_deps = v.into_mut();
                                 smaller_deps.larger.insert(*larger);
-                                // FIXME(#120456) - is `swap_remove` correct?
                                 smaller_deps.larger.swap_remove(&target);
                             }
 
-                            if let Entry::Occupied(v) = vid_map.entry(*larger) {
+                            if let IndexEntry::Occupied(v) = vid_map.entry(*larger) {
                                 let larger_deps = v.into_mut();
                                 larger_deps.smaller.insert(*smaller);
-                                // FIXME(#120456) - is `swap_remove` correct?
                                 larger_deps.smaller.swap_remove(&target);
                             }
                         }
@@ -542,17 +532,15 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                             // Do nothing; we don't care about regions that are smaller than vids.
                         }
                         (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
-                            if let Entry::Occupied(v) = vid_map.entry(*smaller) {
+                            if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) {
                                 let smaller_deps = v.into_mut();
                                 smaller_deps.larger.insert(*larger);
-                                // FIXME(#120456) - is `swap_remove` correct?
                                 smaller_deps.larger.swap_remove(&target);
                             }
 
-                            if let Entry::Occupied(v) = vid_map.entry(*larger) {
+                            if let IndexEntry::Occupied(v) = vid_map.entry(*larger) {
                                 let larger_deps = v.into_mut();
                                 larger_deps.smaller.insert(*smaller);
-                                // FIXME(#120456) - is `swap_remove` correct?
                                 larger_deps.smaller.swap_remove(&target);
                             }
                         }
@@ -560,6 +548,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                 }
             }
         }
+
         finished_map
     }
 
@@ -588,7 +577,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
         ty: Ty<'_>,
         nested: impl Iterator>,
         computed_preds: &mut FxIndexSet>,
-        fresh_preds: &mut FxHashSet>,
+        fresh_preds: &mut FxIndexSet>,
         predicates: &mut VecDeque>,
         selcx: &mut SelectionContext<'_, 'tcx>,
     ) -> bool {
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index fbc2c3c5af45..acac686a6fca 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -15,18 +15,15 @@ enum RegionTarget<'tcx> {
 
 #[derive(Default, Debug, Clone)]
 struct RegionDeps<'tcx> {
-    larger: FxHashSet>,
-    smaller: FxHashSet>,
+    larger: FxIndexSet>,
+    smaller: FxIndexSet>,
 }
 
 pub(crate) struct AutoTraitFinder<'a, 'tcx> {
     pub(crate) cx: &'a mut core::DocContext<'tcx>,
 }
 
-impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx>
-where
-    'tcx: 'a, // should be an implied bound; rustc bug #98852.
-{
+impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
     pub(crate) fn new(cx: &'a mut core::DocContext<'tcx>) -> Self {
         AutoTraitFinder { cx }
     }
@@ -158,7 +155,7 @@ where
         auto_traits
     }
 
-    fn get_lifetime(region: Region<'_>, names_map: &FxHashMap) -> Lifetime {
+    fn get_lifetime(region: Region<'_>, names_map: &FxIndexMap) -> Lifetime {
         region_name(region)
             .map(|name| {
                 names_map
@@ -181,15 +178,15 @@ where
     /// existing inference/solver code do what we want.
     fn handle_lifetimes<'cx>(
         regions: &RegionConstraintData<'cx>,
-        names_map: &FxHashMap,
+        names_map: &FxIndexMap,
     ) -> ThinVec {
         // Our goal is to 'flatten' the list of constraints by eliminating
         // all intermediate RegionVids. At the end, all constraints should
         // be between Regions (aka region variables). This gives us the information
         // we need to create the Generics.
-        let mut finished: FxHashMap<_, Vec<_>> = Default::default();
+        let mut finished: FxIndexMap<_, Vec<_>> = Default::default();
 
-        let mut vid_map: FxHashMap, RegionDeps<'_>> = Default::default();
+        let mut vid_map: FxIndexMap, RegionDeps<'_>> = Default::default();
 
         // Flattening is done in two parts. First, we insert all of the constraints
         // into a map. Each RegionTarget (either a RegionVid or a Region) maps
@@ -245,8 +242,8 @@ where
         //  constraint, and add it to our list. Since we make sure to never re-add
         //  deleted items, this process will always finish.
         while !vid_map.is_empty() {
-            let target = *vid_map.keys().next().expect("Keys somehow empty");
-            let deps = vid_map.remove(&target).expect("Entry somehow missing");
+            let target = *vid_map.keys().next().unwrap();
+            let deps = vid_map.swap_remove(&target).unwrap();
 
             for smaller in deps.smaller.iter() {
                 for larger in deps.larger.iter() {
@@ -260,30 +257,30 @@ where
                             }
                         }
                         (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
-                            if let Entry::Occupied(v) = vid_map.entry(*smaller) {
+                            if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) {
                                 let smaller_deps = v.into_mut();
                                 smaller_deps.larger.insert(*larger);
-                                smaller_deps.larger.remove(&target);
+                                smaller_deps.larger.swap_remove(&target);
                             }
                         }
                         (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
-                            if let Entry::Occupied(v) = vid_map.entry(*larger) {
+                            if let IndexEntry::Occupied(v) = vid_map.entry(*larger) {
                                 let deps = v.into_mut();
                                 deps.smaller.insert(*smaller);
-                                deps.smaller.remove(&target);
+                                deps.smaller.swap_remove(&target);
                             }
                         }
                         (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
-                            if let Entry::Occupied(v) = vid_map.entry(*smaller) {
+                            if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) {
                                 let smaller_deps = v.into_mut();
                                 smaller_deps.larger.insert(*larger);
-                                smaller_deps.larger.remove(&target);
+                                smaller_deps.larger.swap_remove(&target);
                             }
 
-                            if let Entry::Occupied(v) = vid_map.entry(*larger) {
+                            if let IndexEntry::Occupied(v) = vid_map.entry(*larger) {
                                 let larger_deps = v.into_mut();
                                 larger_deps.smaller.insert(*smaller);
-                                larger_deps.smaller.remove(&target);
+                                larger_deps.smaller.swap_remove(&target);
                             }
                         }
                     }
@@ -295,7 +292,7 @@ where
             .iter()
             .flat_map(|(name, lifetime)| {
                 let empty = Vec::new();
-                let bounds: FxHashSet = finished
+                let bounds: FxIndexSet = finished
                     .get(name)
                     .unwrap_or(&empty)
                     .iter()
@@ -315,7 +312,7 @@ where
         lifetime_predicates
     }
 
-    fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxHashSet {
+    fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxIndexSet {
         let bound_predicate = pred.kind();
         let tcx = self.cx.tcx;
         let regions =
@@ -324,7 +321,7 @@ where
                     .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_trait_pred)),
                 ty::ClauseKind::Projection(poly_proj_pred) => tcx
                     .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_proj_pred)),
-                _ => return FxHashSet::default(),
+                _ => return FxIndexSet::default(),
             };
 
         regions
@@ -342,9 +339,9 @@ where
 
     fn make_final_bounds(
         &self,
-        ty_to_bounds: FxHashMap>,
-        ty_to_fn: FxHashMap)>,
-        lifetime_to_bounds: FxHashMap>,
+        ty_to_bounds: FxIndexMap>,
+        ty_to_fn: FxIndexMap)>,
+        lifetime_to_bounds: FxIndexMap>,
     ) -> Vec {
         ty_to_bounds
             .into_iter()
@@ -390,20 +387,16 @@ where
                     return None;
                 }
 
-                let mut bounds_vec = bounds.into_iter().collect();
-                self.sort_where_bounds(&mut bounds_vec);
-
                 Some(WherePredicate::BoundPredicate {
                     ty,
-                    bounds: bounds_vec,
+                    bounds: bounds.into_iter().collect(),
                     bound_params: Vec::new(),
                 })
             })
             .chain(lifetime_to_bounds.into_iter().filter(|(_, bounds)| !bounds.is_empty()).map(
-                |(lifetime, bounds)| {
-                    let mut bounds_vec = bounds.into_iter().collect();
-                    self.sort_where_bounds(&mut bounds_vec);
-                    WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec }
+                |(lifetime, bounds)| WherePredicate::RegionPredicate {
+                    lifetime,
+                    bounds: bounds.into_iter().collect(),
                 },
             ))
             .collect()
@@ -418,19 +411,14 @@ where
     /// * `Fn` bounds are handled specially - instead of leaving it as `T: Fn(),  =
     /// K`, we use the dedicated syntax `T: Fn() -> K`
     /// * We explicitly add a `?Sized` bound if we didn't find any `Sized` predicates for a type
+    #[instrument(level = "debug", skip(self, vid_to_region))]
     fn param_env_to_generics(
         &mut self,
         item_def_id: DefId,
         param_env: ty::ParamEnv<'tcx>,
         mut existing_predicates: ThinVec,
-        vid_to_region: FxHashMap>,
+        vid_to_region: FxIndexMap>,
     ) -> Generics {
-        debug!(
-            "param_env_to_generics(item_def_id={:?}, param_env={:?}, \
-             existing_predicates={:?})",
-            item_def_id, param_env, existing_predicates
-        );
-
         let tcx = self.cx.tcx;
 
         // The `Sized` trait must be handled specially, since we only display it when
@@ -439,6 +427,7 @@ where
 
         let mut replacer = RegionReplacer { vid_to_region: &vid_to_region, tcx };
 
+        // FIXME(fmease): Remove this!
         let orig_bounds: FxHashSet<_> = tcx.param_env(item_def_id).caller_bounds().iter().collect();
         let clean_where_predicates = param_env
             .caller_bounds()
@@ -461,12 +450,11 @@ where
 
         debug!("param_env_to_generics({item_def_id:?}): generic_params={generic_params:?}");
 
-        let mut has_sized = FxHashSet::default();
-        let mut ty_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default();
-        let mut lifetime_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default();
-        let mut ty_to_traits: FxHashMap> = Default::default();
-
-        let mut ty_to_fn: FxHashMap)> = Default::default();
+        let mut has_sized = FxHashSet::default(); // NOTE(fmease): not used for iteration
+        let mut ty_to_bounds = FxIndexMap::<_, FxIndexSet<_>>::default();
+        let mut lifetime_to_bounds = FxIndexMap::<_, FxIndexSet<_>>::default();
+        let mut ty_to_traits = FxIndexMap::>::default();
+        let mut ty_to_fn = FxIndexMap::)>::default();
 
         // FIXME: This code shares much of the logic found in `clean_ty_generics` and
         //        `simplify::where_clause`. Consider deduplicating it to avoid diverging
@@ -526,7 +514,7 @@ where
                                 // that we don't end up with duplicate bounds (e.g., for<'b, 'b>)
                                 for_generics.extend(p.generic_params.drain(..));
                                 p.generic_params.extend(for_generics);
-                                self.is_fn_trait(&p.trait_)
+                                tcx.is_fn_trait(p.trait_.def_id())
                             }
                             _ => false,
                         };
@@ -559,7 +547,7 @@ where
                             let ty = &*self_type;
                             let mut new_trait = trait_.clone();
 
-                            if self.is_fn_trait(trait_) && assoc.name == sym::Output {
+                            if tcx.is_fn_trait(trait_.def_id()) && assoc.name == sym::Output {
                                 ty_to_fn
                                     .entry(ty.clone())
                                     .and_modify(|e| {
@@ -611,7 +599,7 @@ where
                             // that we don't see a
                             // duplicate bound like `T: Iterator + Iterator`
                             // on the docs page.
-                            bounds.remove(&GenericBound::TraitBound(
+                            bounds.swap_remove(&GenericBound::TraitBound(
                                 PolyTrait { trait_: trait_.clone(), generic_params: Vec::new() },
                                 hir::TraitBoundModifier::None,
                             ));
@@ -647,75 +635,8 @@ where
             }
         }
 
-        self.sort_where_predicates(&mut existing_predicates);
-
         Generics { params: generic_params, where_predicates: existing_predicates }
     }
-
-    /// Ensure that the predicates are in a consistent order. The precise
-    /// ordering doesn't actually matter, but it's important that
-    /// a given set of predicates always appears in the same order -
-    /// both for visual consistency between 'rustdoc' runs, and to
-    /// make writing tests much easier
-    #[inline]
-    fn sort_where_predicates(&self, predicates: &mut [WherePredicate]) {
-        // We should never have identical bounds - and if we do,
-        // they're visually identical as well. Therefore, using
-        // an unstable sort is fine.
-        self.unstable_debug_sort(predicates);
-    }
-
-    /// Ensure that the bounds are in a consistent order. The precise
-    /// ordering doesn't actually matter, but it's important that
-    /// a given set of bounds always appears in the same order -
-    /// both for visual consistency between 'rustdoc' runs, and to
-    /// make writing tests much easier
-    #[inline]
-    fn sort_where_bounds(&self, bounds: &mut Vec) {
-        // We should never have identical bounds - and if we do,
-        // they're visually identical as well. Therefore, using
-        // an unstable sort is fine.
-        self.unstable_debug_sort(bounds);
-    }
-
-    /// This might look horrendously hacky, but it's actually not that bad.
-    ///
-    /// For performance reasons, we use several different FxHashMaps
-    /// in the process of computing the final set of where predicates.
-    /// However, the iteration order of a HashMap is completely unspecified.
-    /// In fact, the iteration of an FxHashMap can even vary between platforms,
-    /// since FxHasher has different behavior for 32-bit and 64-bit platforms.
-    ///
-    /// Obviously, it's extremely undesirable for documentation rendering
-    /// to be dependent on the platform it's run on. Apart from being confusing
-    /// to end users, it makes writing tests much more difficult, as predicates
-    /// can appear in any order in the final result.
-    ///
-    /// To solve this problem, we sort WherePredicates and GenericBounds
-    /// by their Debug string. The thing to keep in mind is that we don't really
-    /// care what the final order is - we're synthesizing an impl or bound
-    /// ourselves, so any order can be considered equally valid. By sorting the
-    /// predicates and bounds, however, we ensure that for a given codebase, all
-    /// auto-trait impls always render in exactly the same way.
-    ///
-    /// Using the Debug implementation for sorting prevents us from needing to
-    /// write quite a bit of almost entirely useless code (e.g., how should two
-    /// Types be sorted relative to each other). It also allows us to solve the
-    /// problem for both WherePredicates and GenericBounds at the same time. This
-    /// approach is probably somewhat slower, but the small number of items
-    /// involved (impls rarely have more than a few bounds) means that it
-    /// shouldn't matter in practice.
-    fn unstable_debug_sort(&self, vec: &mut [T]) {
-        vec.sort_by_cached_key(|x| format!("{x:?}"))
-    }
-
-    fn is_fn_trait(&self, path: &Path) -> bool {
-        let tcx = self.cx.tcx;
-        let did = path.def_id();
-        did == tcx.require_lang_item(LangItem::Fn, None)
-            || did == tcx.require_lang_item(LangItem::FnMut, None)
-            || did == tcx.require_lang_item(LangItem::FnOnce, None)
-    }
 }
 
 fn region_name(region: Region<'_>) -> Option {
@@ -727,7 +648,7 @@ fn region_name(region: Region<'_>) -> Option {
 
 /// Replaces all [`ty::RegionVid`]s in a type with [`ty::Region`]s, using the provided map.
 struct RegionReplacer<'a, 'tcx> {
-    vid_to_region: &'a FxHashMap>,
+    vid_to_region: &'a FxIndexMap>,
     tcx: TyCtxt<'tcx>,
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 0cdf52bfb004..9023d9d41179 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -35,7 +35,6 @@ use rustc_span::{self, ExpnKind};
 use rustc_trait_selection::traits::wf::object_region_bounds;
 
 use std::borrow::Cow;
-use std::collections::hash_map::Entry;
 use std::collections::BTreeMap;
 use std::hash::Hash;
 use std::mem;
diff --git a/tests/rustdoc/synthetic_auto/complex.rs b/tests/rustdoc/synthetic_auto/complex.rs
index 4c39f0bf1e07..21055287c996 100644
--- a/tests/rustdoc/synthetic_auto/complex.rs
+++ b/tests/rustdoc/synthetic_auto/complex.rs
@@ -21,8 +21,8 @@ mod foo {
 
 // @has complex/struct.NotOuter.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
-// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K>where K: for<'b> Fn((&'b bool, &'a u8)) \
-// -> &'b i8, T: MyTrait<'a>, >::MyItem: Copy, 'a: 'static"
+// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K>where 'a: 'static, T: MyTrait<'a>, \
+// K: for<'b> Fn((&'b bool, &'a u8)) -> &'b i8, >::MyItem: Copy,"
 
 pub use foo::{Foo, Inner as NotInner, MyTrait as NotMyTrait, Outer as NotOuter};
 
diff --git a/tests/rustdoc/synthetic_auto/lifetimes.rs b/tests/rustdoc/synthetic_auto/lifetimes.rs
index 71265b3078a0..23e1efdaeef1 100644
--- a/tests/rustdoc/synthetic_auto/lifetimes.rs
+++ b/tests/rustdoc/synthetic_auto/lifetimes.rs
@@ -10,7 +10,7 @@ where
 
 // @has lifetimes/struct.Foo.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
-// "impl<'c, K> Send for Foo<'c, K>where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static"
+// "impl<'c, K> Send for Foo<'c, K>where 'c: 'static, K: for<'b> Fn(&'b bool) -> &'c u8,"
 //
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
 // "impl<'c, K> Sync for Foo<'c, K>where K: Sync"
diff --git a/tests/rustdoc/synthetic_auto/no-redundancy.rs b/tests/rustdoc/synthetic_auto/no-redundancy.rs
index d30b38dd4dc1..64dab429647a 100644
--- a/tests/rustdoc/synthetic_auto/no-redundancy.rs
+++ b/tests/rustdoc/synthetic_auto/no-redundancy.rs
@@ -1,6 +1,3 @@
-// FIXME(fmease, #119216): Reenable this test!
-//@ ignore-test
-
 pub struct Inner {
     field: T,
 }
@@ -13,7 +10,7 @@ where
 
 // @has no_redundancy/struct.Outer.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
-// "impl Send for Outerwhere T: Send + Copy"
+// "impl Send for Outerwhere T: Copy + Send"
 pub struct Outer {
     inner_field: Inner,
 }
diff --git a/tests/rustdoc/synthetic_auto/project.rs b/tests/rustdoc/synthetic_auto/project.rs
index 7c9412ae9624..f4ede76e6deb 100644
--- a/tests/rustdoc/synthetic_auto/project.rs
+++ b/tests/rustdoc/synthetic_auto/project.rs
@@ -24,11 +24,11 @@ where
 
 // @has project/struct.Foo.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
-// "impl<'c, K> Send for Foo<'c, K>where K: MyTrait, 'c: 'static"
+// "impl<'c, K> Send for Foo<'c, K>where 'c: 'static, K: MyTrait,"
 //
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
-// "impl<'c, K> Sync for Foo<'c, K>where K: MyTrait, ::MyItem: OtherTrait, \
-// 'c: 'static,"
+// "impl<'c, K> Sync for Foo<'c, K>where 'c: 'static, K: MyTrait, \
+// ::MyItem: OtherTrait,"
 pub struct Foo<'c, K: 'c> {
     inner_field: Inner<'c, K>,
 }

From 652f6f71eb2f0e09849cf416b4d33cb1078846eb Mon Sep 17 00:00:00 2001
From: James Farrell 
Date: Mon, 1 Apr 2024 16:56:43 +0000
Subject: [PATCH 162/192] Pass RUST_BACKTRACE when running docker.

---
 src/ci/docker/run.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh
index 740eb7504f87..9d72fd8a55a7 100755
--- a/src/ci/docker/run.sh
+++ b/src/ci/docker/run.sh
@@ -323,6 +323,7 @@ docker \
   --env GITHUB_ACTIONS \
   --env GITHUB_REF \
   --env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \
+  --env RUST_BACKTRACE \
   --env TOOLSTATE_REPO_ACCESS_TOKEN \
   --env TOOLSTATE_REPO \
   --env TOOLSTATE_PUBLISH \

From 59120d0ef54fdb860cb88a9169eb3ff71d2799b3 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Mon, 1 Apr 2024 21:11:22 +0200
Subject: [PATCH 163/192] Update to new browser-ui-test version

---
 .../x86_64-gnu-tools/browser-ui-test.version  |   2 +-
 tests/rustdoc-gui/anchors.goml                |   2 +-
 tests/rustdoc-gui/code-color.goml             |   2 +-
 tests/rustdoc-gui/codeblock-tooltip.goml      |   2 +-
 tests/rustdoc-gui/cursor.goml                 |   2 +-
 .../docblock-code-block-line-number.goml      |   2 +-
 tests/rustdoc-gui/docblock-table.goml         |   2 +-
 tests/rustdoc-gui/escape-key.goml             |   2 +-
 tests/rustdoc-gui/globals.goml                |   2 +-
 tests/rustdoc-gui/go-to-collapsed-elem.goml   |   4 +-
 tests/rustdoc-gui/headers-color.goml          |   2 +-
 tests/rustdoc-gui/headings-anchor.goml        |   8 +-
 tests/rustdoc-gui/headings.goml               |  10 +-
 tests/rustdoc-gui/help-page.goml              |   8 +-
 tests/rustdoc-gui/highlight-colors.goml       |   4 +-
 tests/rustdoc-gui/item-decl-colors.goml       |   4 +-
 .../item-decl-comment-highlighting.goml       |   4 +-
 tests/rustdoc-gui/item-info-alignment.goml    |   4 +-
 tests/rustdoc-gui/item-info.goml              |   4 +-
 tests/rustdoc-gui/jump-to-def-background.goml |  17 +-
 tests/rustdoc-gui/label-next-to-symbol.goml   |  12 +-
 tests/rustdoc-gui/links-color.goml            |   4 +-
 tests/rustdoc-gui/notable-trait.goml          |  20 +-
 tests/rustdoc-gui/pocket-menu.goml            |   2 +-
 tests/rustdoc-gui/run-on-hover.goml           |   2 +-
 tests/rustdoc-gui/rust-logo.goml              |   2 +-
 tests/rustdoc-gui/scrape-examples-color.goml  |   6 +-
 tests/rustdoc-gui/scrape-examples-toggle.goml |   2 +-
 tests/rustdoc-gui/search-corrections.goml     |  10 +-
 tests/rustdoc-gui/search-error.goml           |   2 +-
 tests/rustdoc-gui/search-filter.goml          |   2 +-
 tests/rustdoc-gui/search-form-elements.goml   |   4 +-
 tests/rustdoc-gui/search-keyboard.goml        |   2 +-
 tests/rustdoc-gui/search-no-result.goml       |   2 +-
 tests/rustdoc-gui/search-reexport.goml        |   4 +-
 tests/rustdoc-gui/search-result-color.goml    | 246 +++++++++---------
 tests/rustdoc-gui/search-result-display.goml  |   4 +-
 .../search-result-impl-disambiguation.goml    |   4 +-
 tests/rustdoc-gui/search-result-keyword.goml  |   2 +-
 .../search-tab-change-title-fn-sig.goml       |  10 +-
 tests/rustdoc-gui/search-tab.goml             |  12 +-
 ...setting-auto-hide-content-large-items.goml |   2 +-
 .../setting-auto-hide-item-methods-docs.goml  |   2 +-
 ...tting-auto-hide-trait-implementations.goml |   2 +-
 .../setting-go-to-only-result.goml            |   4 +-
 tests/rustdoc-gui/settings.goml               |   2 +-
 tests/rustdoc-gui/sidebar-links-color.goml    |   4 +-
 tests/rustdoc-gui/sidebar-mobile.goml         |   2 +-
 .../sidebar-source-code-display.goml          |   4 +-
 tests/rustdoc-gui/sidebar-source-code.goml    |   2 +-
 tests/rustdoc-gui/sidebar.goml                |   2 +-
 tests/rustdoc-gui/source-code-page.goml       |   6 +-
 tests/rustdoc-gui/stab-badge.goml             |   2 +-
 tests/rustdoc-gui/target.goml                 |   2 +-
 tests/rustdoc-gui/toggle-docs.goml            |   2 +-
 .../rustdoc-gui/type-declation-overflow.goml  |   8 +-
 tests/rustdoc-gui/unsafe-fn.goml              |  17 +-
 tests/rustdoc-gui/warning-block.goml          |   2 +-
 tests/rustdoc-gui/where-whitespace.goml       |  10 +-
 59 files changed, 267 insertions(+), 249 deletions(-)

diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
index a2b63962ba11..07feb8234926 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
@@ -1 +1 @@
-0.16.11
\ No newline at end of file
+0.17.0
\ No newline at end of file
diff --git a/tests/rustdoc-gui/anchors.goml b/tests/rustdoc-gui/anchors.goml
index 72e0bcd77e08..3239e54a866b 100644
--- a/tests/rustdoc-gui/anchors.goml
+++ b/tests/rustdoc-gui/anchors.goml
@@ -2,7 +2,7 @@
 
 define-function: (
     "check-colors",
-    (theme, main_color, title_color, main_heading_color, main_heading_type_color, src_link_color, sidebar_link_color),
+    [theme, main_color, title_color, main_heading_color, main_heading_type_color, src_link_color, sidebar_link_color],
     block {
         go-to: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
         // This is needed to ensure that the text color is computed.
diff --git a/tests/rustdoc-gui/code-color.goml b/tests/rustdoc-gui/code-color.goml
index 92bdfb25b001..e17af5e7f1fa 100644
--- a/tests/rustdoc-gui/code-color.goml
+++ b/tests/rustdoc-gui/code-color.goml
@@ -8,7 +8,7 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (theme, doc_code_color, doc_inline_code_color),
+    [theme, doc_code_color, doc_inline_code_color],
     block {
         // Set the theme.
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
diff --git a/tests/rustdoc-gui/codeblock-tooltip.goml b/tests/rustdoc-gui/codeblock-tooltip.goml
index 7be5e39ba472..19e3927f6429 100644
--- a/tests/rustdoc-gui/codeblock-tooltip.goml
+++ b/tests/rustdoc-gui/codeblock-tooltip.goml
@@ -4,7 +4,7 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (theme, background, color, border),
+    [theme, background, color, border],
     block {
         // Setting the theme.
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
diff --git a/tests/rustdoc-gui/cursor.goml b/tests/rustdoc-gui/cursor.goml
index 27c955f5a13a..9412987fc323 100644
--- a/tests/rustdoc-gui/cursor.goml
+++ b/tests/rustdoc-gui/cursor.goml
@@ -8,7 +8,7 @@ assert-css: ("#toggle-all-docs", {"cursor": "pointer"})
 assert-css: ("#copy-path", {"cursor": "pointer"})
 
 // the search tabs
-write: (".search-input", "Foo")
+write-into: (".search-input", "Foo")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml
index a50449e17010..cb7bdaab4c8d 100644
--- a/tests/rustdoc-gui/docblock-code-block-line-number.goml
+++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml
@@ -10,7 +10,7 @@ assert-false: "pre.example-line-numbers"
 // Let's now check some CSS properties...
 define-function: (
     "check-colors",
-    (theme, color),
+    [theme, color],
     block {
         // We now set the setting to show the line numbers on code examples.
         set-local-storage: {
diff --git a/tests/rustdoc-gui/docblock-table.goml b/tests/rustdoc-gui/docblock-table.goml
index 678b302f22e3..db6d065a4b33 100644
--- a/tests/rustdoc-gui/docblock-table.goml
+++ b/tests/rustdoc-gui/docblock-table.goml
@@ -6,7 +6,7 @@ compare-elements-css: (".impl-items .docblock table td", ".top-doc .docblock tab
 
 define-function: (
     "check-colors",
-    (theme, border_color, zebra_stripe_color),
+    [theme, border_color, zebra_stripe_color],
     block {
         set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}
         reload:
diff --git a/tests/rustdoc-gui/escape-key.goml b/tests/rustdoc-gui/escape-key.goml
index 3ea20fd118e6..ff8557b9b81c 100644
--- a/tests/rustdoc-gui/escape-key.goml
+++ b/tests/rustdoc-gui/escape-key.goml
@@ -2,7 +2,7 @@
 // current content displayed.
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 // First, we check that the search results are hidden when the Escape key is pressed.
-write: (".search-input", "test")
+write-into: (".search-input", "test")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 wait-for: "#search h1" // The search element is empty before the first search
diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml
index c01c8bb10191..f8c495ec18a6 100644
--- a/tests/rustdoc-gui/globals.goml
+++ b/tests/rustdoc-gui/globals.goml
@@ -10,7 +10,7 @@ assert-window-property: {"srcIndex": null}
 
 // Form input
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-write: (".search-input", "Foo")
+write-into: (".search-input", "Foo")
 press-key: 'Enter'
 wait-for: "#search-tabs"
 assert-window-property-false: {"searchIndex": null}
diff --git a/tests/rustdoc-gui/go-to-collapsed-elem.goml b/tests/rustdoc-gui/go-to-collapsed-elem.goml
index 80e9791775e4..e56e7ba08cd8 100644
--- a/tests/rustdoc-gui/go-to-collapsed-elem.goml
+++ b/tests/rustdoc-gui/go-to-collapsed-elem.goml
@@ -9,14 +9,14 @@ set-property: ("#implementations-list .implementors-toggle", {"open": "false"})
 click: "//*[@class='sidebar']//a[@href='#method.must_use']"
 assert-property: ("#implementations-list .implementors-toggle", {"open": "true"})
 
-define-function: ("collapsed-from-search", (), block {
+define-function: ("collapsed-from-search", [], block {
     // Now we do the same through search result.
     // First we reload the page without the anchor in the URL.
     go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
     // Then we collapse the section again...
     set-property: ("#implementations-list .implementors-toggle", {"open": "false"})
     // Then we run the search.
-    write: (".search-input", "foo::must_use")
+    write-into: (".search-input", "foo::must_use")
     wait-for: "//*[@id='search']//a[@href='../test_docs/struct.Foo.html#method.must_use']"
     click: "//*[@id='search']//a[@href='../test_docs/struct.Foo.html#method.must_use']"
     assert-property: ("#implementations-list .implementors-toggle", {"open": "true"})
diff --git a/tests/rustdoc-gui/headers-color.goml b/tests/rustdoc-gui/headers-color.goml
index 80d11c9c849c..2a181c0669fe 100644
--- a/tests/rustdoc-gui/headers-color.goml
+++ b/tests/rustdoc-gui/headers-color.goml
@@ -2,7 +2,7 @@
 
 define-function: (
     "check-colors",
-    (theme, color, code_header_color, focus_background_color, headings_color),
+    [theme, color, code_header_color, focus_background_color, headings_color],
     block {
         go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
         // This is needed so that the text color is computed.
diff --git a/tests/rustdoc-gui/headings-anchor.goml b/tests/rustdoc-gui/headings-anchor.goml
index f568caa3b07f..9d52c2ac4b5c 100644
--- a/tests/rustdoc-gui/headings-anchor.goml
+++ b/tests/rustdoc-gui/headings-anchor.goml
@@ -4,7 +4,7 @@ show-text: true
 
 define-function: (
     "check-heading-anchor",
-    (heading_id),
+    [heading_id],
     block {
         // The anchor should not be displayed by default.
         assert-css: ("#" + |heading_id| + " .doc-anchor", { "display": "none" })
@@ -27,6 +27,6 @@ move-cursor-to: "#top-doc-prose-title"
 // to prevent it from overlapping with the `[-]` element.
 assert-css: ("#top-doc-prose-title:hover .doc-anchor", { "display": "none" })
 
-call-function: ("check-heading-anchor", ("top-doc-prose-sub-heading"))
-call-function: ("check-heading-anchor", ("top-doc-prose-sub-sub-heading"))
-call-function: ("check-heading-anchor", ("you-know-the-drill"))
+call-function: ("check-heading-anchor", {"heading_id": "top-doc-prose-sub-heading"})
+call-function: ("check-heading-anchor", {"heading_id": "top-doc-prose-sub-sub-heading"})
+call-function: ("check-heading-anchor", {"heading_id": "you-know-the-drill"})
diff --git a/tests/rustdoc-gui/headings.goml b/tests/rustdoc-gui/headings.goml
index 102b699b1dd1..cdc61e36be20 100644
--- a/tests/rustdoc-gui/headings.goml
+++ b/tests/rustdoc-gui/headings.goml
@@ -156,7 +156,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
 
 define-function: (
     "check-colors",
-    (theme, heading_color, small_heading_color, heading_border_color),
+    [theme, heading_color, small_heading_color, heading_border_color],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
@@ -220,7 +220,7 @@ call-function: (
 
 define-function: (
     "check-since-color",
-    (theme),
+    [theme],
     block {
         set-local-storage: {"rustdoc-theme": |theme|}
         reload:
@@ -229,6 +229,6 @@ define-function: (
 )
 
 go-to: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
-call-function: ("check-since-color", ("ayu"))
-call-function: ("check-since-color", ("dark"))
-call-function: ("check-since-color", ("light"))
+call-function: ("check-since-color", {"theme": "ayu"})
+call-function: ("check-since-color", {"theme": "dark"})
+call-function: ("check-since-color", {"theme": "light"})
diff --git a/tests/rustdoc-gui/help-page.goml b/tests/rustdoc-gui/help-page.goml
index 84c20355500a..9a7247a737ba 100644
--- a/tests/rustdoc-gui/help-page.goml
+++ b/tests/rustdoc-gui/help-page.goml
@@ -7,17 +7,17 @@ assert-css: ("#help dd", {"font-size": "16px"})
 click: "#help-button > a"
 assert-css: ("#help", {"display": "block"})
 compare-elements-property: (".sub", "#help", ["offsetWidth"])
-compare-elements-position: (".sub", "#help", ("x"))
+compare-elements-position: (".sub", "#help", ["x"])
 set-window-size: (500, 1000) // Try mobile next.
 assert-css: ("#help", {"display": "block"})
 compare-elements-property: (".sub", "#help", ["offsetWidth"])
-compare-elements-position: (".sub", "#help", ("x"))
+compare-elements-position: (".sub", "#help", ["x"])
 
 // Checking the color of the elements of the help menu.
 show-text: true
 define-function: (
     "check-colors",
-    (theme, color, background, box_shadow),
+    [theme, color, background, box_shadow],
     block {
         // Setting the theme.
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
@@ -60,7 +60,7 @@ assert-css: ("#help dd", {"font-size": "16px"})
 click: "#help-button > a"
 assert-css: ("#help", {"display": "none"})
 compare-elements-property-false: (".sub", "#help", ["offsetWidth"])
-compare-elements-position-false: (".sub", "#help", ("x"))
+compare-elements-position-false: (".sub", "#help", ["x"])
 
 // This test ensures that the "the rustdoc book" anchor link within the help popover works.
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
diff --git a/tests/rustdoc-gui/highlight-colors.goml b/tests/rustdoc-gui/highlight-colors.goml
index d162674fa697..48bef319d42f 100644
--- a/tests/rustdoc-gui/highlight-colors.goml
+++ b/tests/rustdoc-gui/highlight-colors.goml
@@ -4,7 +4,7 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (
+    [
         theme,
         kw,
         kw2,
@@ -20,7 +20,7 @@ define-function: (
         question_mark,
         comment,
         doc_comment,
-    ),
+    ],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
diff --git a/tests/rustdoc-gui/item-decl-colors.goml b/tests/rustdoc-gui/item-decl-colors.goml
index 7bbd20c4ee00..e68d206a5118 100644
--- a/tests/rustdoc-gui/item-decl-colors.goml
+++ b/tests/rustdoc-gui/item-decl-colors.goml
@@ -6,7 +6,7 @@ fail-on-request-error: false
 
 define-function: (
     "check-colors",
-    (
+    [
         theme,
         attr_color,
         trait_color,
@@ -16,7 +16,7 @@ define-function: (
         constant_color,
         fn_color,
         assoc_type_color,
-    ),
+    ],
     block {
         go-to: "file://" + |DOC_PATH| + "/test_docs/struct.WithGenerics.html"
         show-text: true
diff --git a/tests/rustdoc-gui/item-decl-comment-highlighting.goml b/tests/rustdoc-gui/item-decl-comment-highlighting.goml
index 60772693d6c5..056b6a5b1e81 100644
--- a/tests/rustdoc-gui/item-decl-comment-highlighting.goml
+++ b/tests/rustdoc-gui/item-decl-comment-highlighting.goml
@@ -4,7 +4,7 @@ show-text: true
 
 define-function: (
     "check-item-decl-comment",
-    (theme, url, comment_color),
+    [theme, url, comment_color],
     block {
         go-to: |url|
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
@@ -15,7 +15,7 @@ define-function: (
 
 define-function: (
     "check-items-for-theme",
-    (theme, comment_color),
+    [theme, comment_color],
     block {
         call-function: ("check-item-decl-comment", {
             "theme": |theme|,
diff --git a/tests/rustdoc-gui/item-info-alignment.goml b/tests/rustdoc-gui/item-info-alignment.goml
index 6fc365d1f194..cd0624056b9a 100644
--- a/tests/rustdoc-gui/item-info-alignment.goml
+++ b/tests/rustdoc-gui/item-info-alignment.goml
@@ -4,7 +4,7 @@ go-to: "file://" + |DOC_PATH| + "/lib2/struct.ItemInfoAlignmentTest.html"
 
 // First, we try it in "desktop" mode.
 set-window-size: (1200, 870)
-compare-elements-position: (".impl-items > .item-info", "summary > .item-info", ("x"))
+compare-elements-position: (".impl-items > .item-info", "summary > .item-info", ["x"])
 // Next, we try it in "mobile" mode (max-width: 700px).
 set-window-size: (650, 650)
-compare-elements-position: (".impl-items > .item-info", "summary > .item-info", ("x"))
+compare-elements-position: (".impl-items > .item-info", "summary > .item-info", ["x"])
diff --git a/tests/rustdoc-gui/item-info.goml b/tests/rustdoc-gui/item-info.goml
index b46d4255ee51..1eb46e832b70 100644
--- a/tests/rustdoc-gui/item-info.goml
+++ b/tests/rustdoc-gui/item-info.goml
@@ -31,13 +31,13 @@ assert-count: ("#main-content > .item-info .stab", 2)
 compare-elements-position-false: (
     "#main-content > .item-info .stab:nth-of-type(1)",
     "#main-content > .item-info .stab:nth-of-type(2)",
-    ("y"),
+    ["y"],
 )
 // But they should have the same `x` position.
 compare-elements-position: (
     "#main-content > .item-info .stab:nth-of-type(1)",
     "#main-content > .item-info .stab:nth-of-type(2)",
-    ("x"),
+    ["x"],
 )
 // They are supposed to have the same height too.
 compare-elements-css: (
diff --git a/tests/rustdoc-gui/jump-to-def-background.goml b/tests/rustdoc-gui/jump-to-def-background.goml
index fa7ed3586dd5..ae9c0c560cf3 100644
--- a/tests/rustdoc-gui/jump-to-def-background.goml
+++ b/tests/rustdoc-gui/jump-to-def-background.goml
@@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/src/link_to_definition/lib.rs.html"
 
 define-function: (
     "check-background-color",
-    (theme, background_color),
+    [theme, background_color],
     block {
         // Set the theme.
         set-local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false" }
@@ -17,6 +17,15 @@ define-function: (
     },
 )
 
-call-function: ("check-background-color", ("ayu", "#333"))
-call-function: ("check-background-color", ("dark", "#333"))
-call-function: ("check-background-color", ("light", "#eee"))
+call-function: ("check-background-color", {
+    "theme": "ayu",
+    "background_color": "#333",
+})
+call-function: ("check-background-color", {
+    "theme": "dark",
+    "background_color": "#333",
+})
+call-function: ("check-background-color", {
+    "theme": "light",
+    "background_color": "#eee",
+})
diff --git a/tests/rustdoc-gui/label-next-to-symbol.goml b/tests/rustdoc-gui/label-next-to-symbol.goml
index d23f9114d361..9a7de60bf38e 100644
--- a/tests/rustdoc-gui/label-next-to-symbol.goml
+++ b/tests/rustdoc-gui/label-next-to-symbol.goml
@@ -27,14 +27,14 @@ compare-elements-position-near: (
 compare-elements-position: (
     ".item-name .stab.deprecated",
     ".item-name .stab.portability",
-    ("y"),
+    ["y"],
 )
 
 // Ensure no wrap
 compare-elements-position: (
     "//*[@class='item-name']//a[text()='replaced_function']/..",
     "//*[@class='desc docblock-short'][text()='a thing with a label']",
-    ("y"),
+    ["y"],
 )
 
 // Mobile view
@@ -49,19 +49,19 @@ compare-elements-position-near: (
 compare-elements-position: (
     ".item-name .stab.deprecated",
     ".item-name .stab.portability",
-    ("y"),
+    ["y"],
 )
 
 // Ensure wrap
 compare-elements-position-false: (
     "//*[@class='item-name']//a[text()='replaced_function']/..",
     "//*[@class='desc docblock-short'][text()='a thing with a label']",
-    ("y"),
+    ["y"],
 )
 compare-elements-position-false: (
     ".item-name .stab.deprecated",
     "//*[@class='desc docblock-short'][text()='a thing with a label']",
-    ("y"),
+    ["y"],
 )
 
 // Ensure it doesn't expand.
@@ -72,5 +72,5 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/cfgs/index.html"
 compare-elements-position-false: (
     "//*[@class='stab portability']/code[text()='appservice-api-c']",
     "//*[@class='stab portability']/code[text()='server']",
-    ("y"),
+    ["y"],
 )
diff --git a/tests/rustdoc-gui/links-color.goml b/tests/rustdoc-gui/links-color.goml
index d88ebfb40d7a..a1fb619a5d34 100644
--- a/tests/rustdoc-gui/links-color.goml
+++ b/tests/rustdoc-gui/links-color.goml
@@ -6,8 +6,8 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (theme, mod, macro, struct, enum, trait, fn, type, union, keyword,
-     sidebar, sidebar_current, sidebar_current_background),
+    [theme, mod, macro, struct, enum, trait, fn, type, union, keyword,
+     sidebar, sidebar_current, sidebar_current_background],
     block {
         set-local-storage: {
             "rustdoc-theme": |theme|,
diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml
index e10bb538f078..0b1c6622596a 100644
--- a/tests/rustdoc-gui/notable-trait.goml
+++ b/tests/rustdoc-gui/notable-trait.goml
@@ -7,13 +7,13 @@ set-window-size: (1100, 600)
 compare-elements-position: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ("y"),
+    ["y"],
 )
 // Checking they don't have the same x position.
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ("x"),
+    ["x"],
 )
 // The `i` should be *after* the type.
 assert-position: (
@@ -37,7 +37,7 @@ compare-elements-position-near: (
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     "//*[@class='tooltip popover']",
-    ("x")
+    ["x"]
 )
 click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
 move-cursor-to: "//h1"
@@ -48,7 +48,7 @@ set-window-size: (1055, 600)
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ("y", "x"),
+    ["y", "x"],
 )
 
 // Now both the `i` and the struct name should be on the next line.
@@ -57,13 +57,13 @@ set-window-size: (980, 600)
 compare-elements-position: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ("y"),
+    ["y"],
 )
 // Checking they don't have the same x position.
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ("x"),
+    ["x"],
 )
 // The `i` should be *after* the type.
 assert-position: (
@@ -81,13 +81,13 @@ set-window-size: (650, 600)
 compare-elements-position: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ("y"),
+    ["y"],
 )
 // Checking they don't have the same x position.
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ("x"),
+    ["x"],
 )
 // The `i` should be *after* the type.
 assert-position: (
@@ -109,7 +109,7 @@ compare-elements-position-near: (
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
     "//*[@class='tooltip popover']",
-    ("x")
+    ["x"]
 )
 assert-position: (
     "//*[@class='tooltip popover']",
@@ -122,7 +122,7 @@ assert-count: ("//*[@class='tooltip popover']", 0)
 // Now check the colors.
 define-function: (
     "check-colors",
-    (theme, header_color, content_color, type_color, trait_color, link_color),
+    [theme, header_color, content_color, type_color, trait_color, link_color],
     block {
         go-to: "file://" + |DOC_PATH| + "/test_docs/struct.NotableStructWithLongName.html"
         // This is needed to ensure that the text color is computed.
diff --git a/tests/rustdoc-gui/pocket-menu.goml b/tests/rustdoc-gui/pocket-menu.goml
index 404e57403059..b16150cd0d36 100644
--- a/tests/rustdoc-gui/pocket-menu.goml
+++ b/tests/rustdoc-gui/pocket-menu.goml
@@ -31,7 +31,7 @@ assert-css: ("#settings-menu .popover", {"display": "none"})
 
 define-function: (
     "check-popover-colors",
-    (theme, border_color),
+    [theme, border_color],
     block {
         set-local-storage: {
             "rustdoc-theme": |theme|,
diff --git a/tests/rustdoc-gui/run-on-hover.goml b/tests/rustdoc-gui/run-on-hover.goml
index 1f87febcec6b..19b15afbac3c 100644
--- a/tests/rustdoc-gui/run-on-hover.goml
+++ b/tests/rustdoc-gui/run-on-hover.goml
@@ -7,7 +7,7 @@ show-text: true
 
 define-function: (
     "check-run-button",
-    (theme, color, background, hover_color, hover_background),
+    [theme, color, background, hover_color, hover_background],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
diff --git a/tests/rustdoc-gui/rust-logo.goml b/tests/rustdoc-gui/rust-logo.goml
index dcf3d6bab638..a3b420e5eb92 100644
--- a/tests/rustdoc-gui/rust-logo.goml
+++ b/tests/rustdoc-gui/rust-logo.goml
@@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/staged_api/index.html"
 
 define-function: (
     "check-logo",
-    (theme, filter),
+    [theme, filter],
     block {
         // Going to the doc page.
         go-to: "file://" + |DOC_PATH| + "/staged_api/index.html"
diff --git a/tests/rustdoc-gui/scrape-examples-color.goml b/tests/rustdoc-gui/scrape-examples-color.goml
index 0052d18dc560..b1675a5f1fd7 100644
--- a/tests/rustdoc-gui/scrape-examples-color.goml
+++ b/tests/rustdoc-gui/scrape-examples-color.goml
@@ -4,8 +4,8 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (theme, highlight, highlight_focus, help_border, help_color, help_hover_border,
-     help_hover_color),
+    [theme, highlight, highlight_focus, help_border, help_color, help_hover_border,
+     help_hover_color],
     block {
         set-local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false", }
         reload:
@@ -64,7 +64,7 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
 
 define-function: (
     "check-background",
-    (theme, background_color_start, background_color_end),
+    [theme, background_color_start, background_color_end],
     block {
         set-local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false", }
         reload:
diff --git a/tests/rustdoc-gui/scrape-examples-toggle.goml b/tests/rustdoc-gui/scrape-examples-toggle.goml
index f742b3186e5b..ea645d289244 100644
--- a/tests/rustdoc-gui/scrape-examples-toggle.goml
+++ b/tests/rustdoc-gui/scrape-examples-toggle.goml
@@ -5,7 +5,7 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
 show-text: true
 define-function: (
     "check-color",
-    (theme, toggle_line_color, toggle_line_hover_color),
+    [theme, toggle_line_color, toggle_line_hover_color],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
diff --git a/tests/rustdoc-gui/search-corrections.goml b/tests/rustdoc-gui/search-corrections.goml
index aeb3c9b31a3f..b81b1f382a95 100644
--- a/tests/rustdoc-gui/search-corrections.goml
+++ b/tests/rustdoc-gui/search-corrections.goml
@@ -4,7 +4,7 @@
 // First, try a search-by-name
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 // Intentionally wrong spelling of "NotableStructWithLongName"
-write: (".search-input", "NotableStructWithLongNamr")
+write-into: (".search-input", "NotableStructWithLongNamr")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -41,7 +41,7 @@ assert-text: (
 // Now, explicit return values
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 // Intentionally wrong spelling of "NotableStructWithLongName"
-write: (".search-input", "-> NotableStructWithLongNamr")
+write-into: (".search-input", "-> NotableStructWithLongNamr")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -58,7 +58,7 @@ assert-text: (
 // Now, generic correction
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 // Intentionally wrong spelling of "NotableStructWithLongName"
-write: (".search-input", "NotableStructWithLongNamr, NotableStructWithLongNamr")
+write-into: (".search-input", "NotableStructWithLongNamr, NotableStructWithLongNamr")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -75,7 +75,7 @@ assert-text: (
 // Now, generic correction plus error
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 // Intentionally wrong spelling of "NotableStructWithLongName"
-write: (".search-input", "Foo,y")
+write-into: (".search-input", "Foo,y")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -91,7 +91,7 @@ assert-text: (
 
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 // Intentionally wrong spelling of "NotableStructWithLongName"
-write: (".search-input", "generic:NotableStructWithLongNamr,y")
+write-into: (".search-input", "generic:NotableStructWithLongNamr,y")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
diff --git a/tests/rustdoc-gui/search-error.goml b/tests/rustdoc-gui/search-error.goml
index 70aeda1769ae..d3de77b5635c 100644
--- a/tests/rustdoc-gui/search-error.goml
+++ b/tests/rustdoc-gui/search-error.goml
@@ -4,7 +4,7 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (theme, error_background),
+    [theme, error_background],
     block {
         // Setting the theme.
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
diff --git a/tests/rustdoc-gui/search-filter.goml b/tests/rustdoc-gui/search-filter.goml
index 9e2855b5e02f..8c50322fcd4e 100644
--- a/tests/rustdoc-gui/search-filter.goml
+++ b/tests/rustdoc-gui/search-filter.goml
@@ -1,7 +1,7 @@
 // Checks that the crate search filtering is handled correctly and changes the results.
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 show-text: true
-write: (".search-input", "test")
+write-into: (".search-input", "test")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
diff --git a/tests/rustdoc-gui/search-form-elements.goml b/tests/rustdoc-gui/search-form-elements.goml
index 0ea61a4f0eb8..2fc66259291c 100644
--- a/tests/rustdoc-gui/search-form-elements.goml
+++ b/tests/rustdoc-gui/search-form-elements.goml
@@ -4,10 +4,10 @@ show-text: true
 
 define-function: (
     "check-search-colors",
-    (
+    [
         theme, border, background, search_input_color, search_input_border_focus,
         menu_button_border, menu_button_a_color, menu_button_a_border_hover, menu_a_color,
-    ),
+    ],
     block {
         set-local-storage: {
             "rustdoc-theme": |theme|,
diff --git a/tests/rustdoc-gui/search-keyboard.goml b/tests/rustdoc-gui/search-keyboard.goml
index f1d8024616bb..707bb8f5faa8 100644
--- a/tests/rustdoc-gui/search-keyboard.goml
+++ b/tests/rustdoc-gui/search-keyboard.goml
@@ -1,7 +1,7 @@
 // Checks that the search tab results work correctly with function signature syntax
 // First, try a search-by-name
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-write: (".search-input", "Foo")
+write-into: (".search-input", "Foo")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
diff --git a/tests/rustdoc-gui/search-no-result.goml b/tests/rustdoc-gui/search-no-result.goml
index e7c647912563..dda50ec3fb64 100644
--- a/tests/rustdoc-gui/search-no-result.goml
+++ b/tests/rustdoc-gui/search-no-result.goml
@@ -4,7 +4,7 @@ show-text: true
 
 define-function: (
     "check-no-result",
-    (theme, link, link_hover),
+    [theme, link, link_hover],
     block {
         // Changing theme.
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
diff --git a/tests/rustdoc-gui/search-reexport.goml b/tests/rustdoc-gui/search-reexport.goml
index b9d2c8f15cee..2e7c967d5c33 100644
--- a/tests/rustdoc-gui/search-reexport.goml
+++ b/tests/rustdoc-gui/search-reexport.goml
@@ -6,7 +6,7 @@ reload:
 // First we check that the reexport has the correct ID and no background color.
 assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;")
 assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
-write: (".search-input", "TheStdReexport")
+write-into: (".search-input", "TheStdReexport")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 wait-for: "//a[@class='result-import']"
@@ -22,7 +22,7 @@ wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "#494a
 // We now check that the alias is working as well on the reexport.
 // To be SURE that the search will be run.
 press-key: 'Enter'
-write: (".search-input", "AliasForTheStdReexport")
+write-into: (".search-input", "AliasForTheStdReexport")
 wait-for: "//a[@class='result-import']"
 assert-text: (
     "a.result-import .result-name",
diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml
index 44677dfbfef4..8b0ca7ec97c4 100644
--- a/tests/rustdoc-gui/search-result-color.goml
+++ b/tests/rustdoc-gui/search-result-color.goml
@@ -2,7 +2,7 @@
 
 define-function: (
     "check-result-color",
-    (result_kind, color, hover_color),
+    [result_kind, color, hover_color],
     block {
         assert-css: (".result-" + |result_kind| + " ." + |result_kind|, {"color": |color|}, ALL)
         assert-css: (
@@ -78,60 +78,60 @@ store-value: (hover_background_color, "#3c3c3c") // hover background color
 store-value: (grey, "#999")
 
 call-function: (
-    "check-result-color", (
-        "keyword", // item kind
-        "#39afd7", // color of item kind
-        "#39afd7", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "keyword", // item kind
+        "color": "#39afd7", // color of item kind
+        "hover_color": "#39afd7", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "struct", // item kind
-        "#ffa0a5", // color of item kind
-        "#ffa0a5", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "struct", // item kind
+        "color": "#ffa0a5", // color of item kind
+        "hover_color": "#ffa0a5", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "associatedtype", // item kind
-        "#39afd7", // color of item kind
-        "#39afd7", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "associatedtype", // item kind
+        "color": "#39afd7", // color of item kind
+        "hover_color": "#39afd7", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "tymethod", // item kind
-        "#fdd687", // color of item kind
-        "#fdd687", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "tymethod", // item kind
+        "color": "#fdd687", // color of item kind
+        "hover_color": "#fdd687", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "method", // item kind
-        "#fdd687", // color of item kind
-        "#fdd687", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "method", // item kind
+        "color": "#fdd687", // color of item kind
+        "hover_color": "#fdd687", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "structfield", // item kind
-        "#0096cf", // color of item kind
-        "#fff", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "structfield", // item kind
+        "color": "#0096cf", // color of item kind
+        "hover_color": "#fff", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "macro", // item kind
-        "#a37acc", // color of item kind
-        "#a37acc", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "macro", // item kind
+        "color": "#a37acc", // color of item kind
+        "hover_color": "#a37acc", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "fn", // item kind
-        "#fdd687", // color of item kind
-        "#fdd687", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "fn", // item kind
+        "color": "#fdd687", // color of item kind
+        "hover_color": "#fdd687", // color of hovered/focused item kind
+    },
 )
 
 // Checking the `` container.
@@ -190,60 +190,60 @@ store-value: (hover_background_color, "#616161") // hover background color
 store-value: (grey, "#ccc")
 
 call-function: (
-    "check-result-color", (
-        "keyword", // item kind
-        "#d2991d", // color of item kind
-        "#d2991d", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "keyword", // item kind
+        "color": "#d2991d", // color of item kind
+        "hover_color": "#d2991d", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "struct", // item kind
-        "#2dbfb8", // color of item kind
-        "#2dbfb8", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "struct", // item kind
+        "color": "#2dbfb8", // color of item kind
+        "hover_color": "#2dbfb8", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "associatedtype", // item kind
-        "#d2991d", // color of item kind
-        "#d2991d", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "associatedtype", // item kind
+        "color": "#d2991d", // color of item kind
+        "hover_color": "#d2991d", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "tymethod", // item kind
-        "#2bab63", // color of item kind
-        "#2bab63", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "tymethod", // item kind
+        "color": "#2bab63", // color of item kind
+        "hover_color": "#2bab63", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "method", // item kind
-        "#2bab63", // color of item kind
-        "#2bab63", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "method", // item kind
+        "color": "#2bab63", // color of item kind
+        "hover_color": "#2bab63", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "structfield", // item kind
-        "#ddd", // color of item kind
-        "#ddd", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "structfield", // item kind
+        "color": "#ddd", // color of item kind
+        "hover_color": "#ddd", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "macro", // item kind
-        "#09bd00", // color of item kind
-        "#09bd00", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "macro", // item kind
+        "color": "#09bd00", // color of item kind
+        "hover_color": "#09bd00", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "fn", // item kind
-        "#2bab63", // color of item kind
-        "#2bab63", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "fn", // item kind
+        "color": "#2bab63", // color of item kind
+        "hover_color": "#2bab63", // color of hovered/focused item kind
+    },
 )
 
 // Checking the `` container.
@@ -287,60 +287,60 @@ store-value: (hover_background_color, "#ccc") // hover background color
 store-value: (grey, "#999")
 
 call-function: (
-    "check-result-color", (
-        "keyword", // item kind
-        "#3873ad", // color of item kind
-        "#3873ad", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "keyword", // item kind
+        "color": "#3873ad", // color of item kind
+        "hover_color": "#3873ad", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "struct", // item kind
-        "#ad378a", // color of item kind
-        "#ad378a", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "struct", // item kind
+        "color": "#ad378a", // color of item kind
+        "hover_color": "#ad378a", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "associatedtype", // item kind
-        "#3873ad", // color of item kind
-        "#3873ad", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "associatedtype", // item kind
+        "color": "#3873ad", // color of item kind
+        "hover_color": "#3873ad", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "tymethod", // item kind
-        "#ad7c37", // color of item kind
-        "#ad7c37", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "tymethod", // item kind
+        "color": "#ad7c37", // color of item kind
+        "hover_color": "#ad7c37", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "method", // item kind
-        "#ad7c37", // color of item kind
-        "#ad7c37", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "method", // item kind
+        "color": "#ad7c37", // color of item kind
+        "hover_color": "#ad7c37", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "structfield", // item kind
-        "#000", // color of item kind
-        "#000", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "structfield", // item kind
+        "color": "#000", // color of item kind
+        "hover_color": "#000", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "macro", // item kind
-        "#068000", // color of item kind
-        "#068000", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "macro", // item kind
+        "color": "#068000", // color of item kind
+        "hover_color": "#068000", // color of hovered/focused item kind
+    },
 )
 call-function: (
-    "check-result-color", (
-        "fn", // item kind
-        "#ad7c37", // color of item kind
-        "#ad7c37", // color of hovered/focused item kind
-    ),
+    "check-result-color", {
+        "result_kind": "fn", // item kind
+        "color": "#ad7c37", // color of item kind
+        "hover_color": "#ad7c37", // color of hovered/focused item kind
+    },
 )
 
 // Checking the `` container.
@@ -358,11 +358,11 @@ show-text: true
 
 define-function: (
     "check-alias",
-    (theme, alias, grey),
+    [theme, alias, grey],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
-        write: (".search-input", "thisisanalias")
+        write-into: (".search-input", "thisisanalias")
         // To be SURE that the search will be run.
         press-key: 'Enter'
         // Waiting for the search results to appear...
diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml
index 6ce13b8c3d30..b1a5548808e0 100644
--- a/tests/rustdoc-gui/search-result-display.goml
+++ b/tests/rustdoc-gui/search-result-display.goml
@@ -2,7 +2,7 @@
 // Checks that the search results have the expected width.
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 set-window-size: (900, 1000)
-write: (".search-input", "test")
+write-into: (".search-input", "test")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 wait-for: "#crate-search"
@@ -69,7 +69,7 @@ assert-css: ("#search", {"width": "640px"})
 show-text: true
 define-function: (
     "check-filter",
-    (theme, border, filter, hover_border, hover_filter),
+    [theme, border, filter, hover_border, hover_filter],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
diff --git a/tests/rustdoc-gui/search-result-impl-disambiguation.goml b/tests/rustdoc-gui/search-result-impl-disambiguation.goml
index 6d12032e891b..3e49ac33025e 100644
--- a/tests/rustdoc-gui/search-result-impl-disambiguation.goml
+++ b/tests/rustdoc-gui/search-result-impl-disambiguation.goml
@@ -5,7 +5,7 @@
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 
 // This should link to the inherent impl
-write: (".search-input", "ZyxwvutMethodDisambiguation -> bool")
+write-into: (".search-input", "ZyxwvutMethodDisambiguation -> bool")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -25,7 +25,7 @@ assert: "section:target"
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 
 // This should link to the trait impl
-write: (".search-input", "ZyxwvutMethodDisambiguation, usize -> usize")
+write-into: (".search-input", "ZyxwvutMethodDisambiguation, usize -> usize")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
diff --git a/tests/rustdoc-gui/search-result-keyword.goml b/tests/rustdoc-gui/search-result-keyword.goml
index 1b2be6d4e3e2..370edce2ddd0 100644
--- a/tests/rustdoc-gui/search-result-keyword.goml
+++ b/tests/rustdoc-gui/search-result-keyword.goml
@@ -1,6 +1,6 @@
 // Checks that the "keyword" results have the expected text alongside them.
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-write: (".search-input", "CookieMonster")
+write-into: (".search-input", "CookieMonster")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
diff --git a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml
index 156d8d03ca2d..7e26229ec6e5 100644
--- a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml
+++ b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml
@@ -1,7 +1,7 @@
 // Checks that the search tab results work correctly with function signature syntax
 // First, try a search-by-name
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-write: (".search-input", "Foo")
+write-into: (".search-input", "Foo")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -23,7 +23,7 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected
 
 // Now try search-by-return
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-write: (".search-input", "-> String")
+write-into: (".search-input", "-> String")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -45,7 +45,7 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected
 
 // Try with a search-by-return with no results
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-write: (".search-input", "-> Something")
+write-into: (".search-input", "-> Something")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -55,7 +55,7 @@ assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types"
 
 // Try with a search-by-parameter
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-write: (".search-input", "usize,pattern")
+write-into: (".search-input", "usize,pattern")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
@@ -65,7 +65,7 @@ assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Parameters",
 
 // Try with a search-by-parameter-and-return
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-write: (".search-input", "pattern -> str")
+write-into: (".search-input", "pattern -> str")
 // To be SURE that the search will be run.
 press-key: 'Enter'
 // Waiting for the search results to appear...
diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml
index b52bb0688c1b..c33866593c35 100644
--- a/tests/rustdoc-gui/search-tab.goml
+++ b/tests/rustdoc-gui/search-tab.goml
@@ -4,9 +4,9 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (theme, background, background_selected, background_hover, border_bottom,
+    [theme, background, background_selected, background_hover, border_bottom,
      border_bottom_selected, border_bottom_hover, border_top, border_top_selected,
-     border_top_hover),
+     border_top_hover],
     block {
         // Setting the theme.
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
@@ -93,12 +93,12 @@ assert-property: ("#search-tabs > button:nth-child(3) > .count", {"offsetWidth":
 compare-elements-position: (
     "#search-tabs > button:nth-child(1) > .count",
     "#search-tabs > button:nth-child(2) > .count",
-    ("y")
+    ["y"]
 )
 compare-elements-position: (
     "#search-tabs > button:nth-child(2) > .count",
     "#search-tabs > button:nth-child(3) > .count",
-    ("y")
+    ["y"]
 )
 // Check that counts are beside the titles and haven't wrapped
 compare-elements-position-near: (
@@ -135,12 +135,12 @@ assert-property: ("#search-tabs > button:nth-child(3) > .count", {"offsetWidth":
 compare-elements-position: (
     "#search-tabs > button:nth-child(1) > .count",
     "#search-tabs > button:nth-child(2) > .count",
-    ("y")
+    ["y"]
 )
 compare-elements-position: (
     "#search-tabs > button:nth-child(2) > .count",
     "#search-tabs > button:nth-child(3) > .count",
-    ("y")
+    ["y"]
 )
 // Check that counts are NOT beside the titles; now they have wrapped
 compare-elements-position-near-false: (
diff --git a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml
index b55a1cfd92bc..9afde7c61da6 100644
--- a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml
+++ b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml
@@ -6,7 +6,7 @@ fail-on-request-error: false
 
 define-function: (
     "check-setting",
-    (storage_value, setting_attribute_value, toggle_attribute_value),
+    [storage_value, setting_attribute_value, toggle_attribute_value],
     block {
         assert-local-storage: {"rustdoc-auto-hide-large-items": |storage_value|}
         click: "#settings-menu"
diff --git a/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml b/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml
index 5210ad8f793b..644396ed5782 100644
--- a/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml
+++ b/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml
@@ -3,7 +3,7 @@
 
 define-function: (
     "check-setting",
-    (storage_value, setting_attribute_value, toggle_attribute_value),
+    [storage_value, setting_attribute_value, toggle_attribute_value],
     block {
         assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|}
         click: "#settings-menu"
diff --git a/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml b/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml
index ecadd8fa80ed..3c09198dae50 100644
--- a/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml
+++ b/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml
@@ -2,7 +2,7 @@
 
 define-function: (
     "check-setting",
-    (storage_value, setting_attribute_value, toggle_attribute_value),
+    [storage_value, setting_attribute_value, toggle_attribute_value],
     block {
         assert-local-storage: {"rustdoc-auto-hide-trait-implementations": |storage_value|}
         click: "#settings-menu"
diff --git a/tests/rustdoc-gui/setting-go-to-only-result.goml b/tests/rustdoc-gui/setting-go-to-only-result.goml
index 45e0b349051e..f8535477c22d 100644
--- a/tests/rustdoc-gui/setting-go-to-only-result.goml
+++ b/tests/rustdoc-gui/setting-go-to-only-result.goml
@@ -2,7 +2,7 @@
 
 define-function: (
     "check-setting",
-    (storage_value, setting_attribute_value),
+    [storage_value, setting_attribute_value],
     block {
         assert-local-storage: {"rustdoc-go-to-only-result": |storage_value|}
         click: "#settings-menu"
@@ -32,7 +32,7 @@ assert-local-storage: {"rustdoc-go-to-only-result": "true"}
 
 go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
 // We enter it into the search.
-write: (".search-input", "HasALongTraitWithParams")
+write-into: (".search-input", "HasALongTraitWithParams")
 wait-for-document-property: {"title": "HasALongTraitWithParams in lib2 - Rust"}
 assert-window-property: ({"location": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH)
 
diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml
index e40c637dcf7d..0bb21c28cb52 100644
--- a/tests/rustdoc-gui/settings.goml
+++ b/tests/rustdoc-gui/settings.goml
@@ -304,7 +304,7 @@ wait-for: "#settings"
 assert-css: (".setting-radio", {"cursor": "pointer"})
 
 assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS)
-compare-elements-position: (".sub form", "#settings", ("x"))
+compare-elements-position: (".sub form", "#settings", ["x"])
 
 // Check that setting-line has the same margin in this mode as in the popover.
 assert-css: (".setting-line", {"margin": |setting_line_margin|})
diff --git a/tests/rustdoc-gui/sidebar-links-color.goml b/tests/rustdoc-gui/sidebar-links-color.goml
index 774fbcac1e2c..0edffc51a816 100644
--- a/tests/rustdoc-gui/sidebar-links-color.goml
+++ b/tests/rustdoc-gui/sidebar-links-color.goml
@@ -6,12 +6,12 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (
+    [
         theme, struct, struct_hover, struct_hover_background, enum, enum_hover,
         enum_hover_background, union, union_hover, union_hover_background, trait, trait_hover,
         trait_hover_background, fn, fn_hover, fn_hover_background, type, type_hover,
         type_hover_background, keyword, keyword_hover, keyword_hover_background,
-    ),
+    ],
     block {
         set-local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false" }
         reload:
diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml
index d3a82d9ebe6f..8843de8d7e94 100644
--- a/tests/rustdoc-gui/sidebar-mobile.goml
+++ b/tests/rustdoc-gui/sidebar-mobile.goml
@@ -57,7 +57,7 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (theme, color, background),
+    [theme, color, background],
     block {
         set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}
         reload:
diff --git a/tests/rustdoc-gui/sidebar-source-code-display.goml b/tests/rustdoc-gui/sidebar-source-code-display.goml
index 5149d4991f73..41c8e45f4a6d 100644
--- a/tests/rustdoc-gui/sidebar-source-code-display.goml
+++ b/tests/rustdoc-gui/sidebar-source-code-display.goml
@@ -30,9 +30,9 @@ show-text: true
 
 define-function: (
     "check-colors",
-    (
+    [
         theme, color, color_hover, background, background_hover, background_toggle,
-    ),
+    ],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml
index d7de43a2243a..3f7ef643d18e 100644
--- a/tests/rustdoc-gui/sidebar-source-code.goml
+++ b/tests/rustdoc-gui/sidebar-source-code.goml
@@ -6,7 +6,7 @@ show-text: true
 // First, check the sidebar colors.
 define-function: (
     "check-colors",
-    (theme, color, background_color),
+    [theme, color, background_color],
     block {
         set-local-storage: {
             "rustdoc-theme": |theme|,
diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml
index 82b4f2e9429c..115b1eb323c4 100644
--- a/tests/rustdoc-gui/sidebar.goml
+++ b/tests/rustdoc-gui/sidebar.goml
@@ -6,7 +6,7 @@ show-text: true
 // First, check the sidebar colors.
 define-function: (
     "check-colors",
-    (theme, color, background_color),
+    [theme, color, background_color],
     block {
         set-local-storage: {
             "rustdoc-theme": |theme|,
diff --git a/tests/rustdoc-gui/source-code-page.goml b/tests/rustdoc-gui/source-code-page.goml
index 8b4d7617e0cd..e29d123d2270 100644
--- a/tests/rustdoc-gui/source-code-page.goml
+++ b/tests/rustdoc-gui/source-code-page.goml
@@ -21,7 +21,7 @@ assert-attribute-false: (".src-line-numbers > a:nth-child(7)", {"class": "line-h
 
 define-function: (
     "check-colors",
-    (theme, color, background_color, highlight_color, highlight_background_color),
+    [theme, color, background_color, highlight_color, highlight_background_color],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
@@ -61,7 +61,7 @@ call-function: ("check-colors", {
 })
 
 // This is to ensure that the content is correctly align with the line numbers.
-compare-elements-position: ("//*[@id='1']", ".rust > code > span", ("y"))
+compare-elements-position: ("//*[@id='1']", ".rust > code > span", ["y"])
 // Check the `href` property so that users can treat anchors as links.
 assert-property: (".src-line-numbers > a:nth-child(1)", {
     "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#1"
@@ -122,7 +122,7 @@ store-property: (
 )
 define-function: (
     "check-sidebar-dir-entry",
-    (x, y),
+    [x, y],
     block {
         assert: "details:first-of-type.dir-entry[open] > summary::marker"
         assert-css: ("#src-sidebar > details:first-of-type.dir-entry", {"padding-left": "4px"})
diff --git a/tests/rustdoc-gui/stab-badge.goml b/tests/rustdoc-gui/stab-badge.goml
index bb3d2aaa3dca..46df0946c459 100644
--- a/tests/rustdoc-gui/stab-badge.goml
+++ b/tests/rustdoc-gui/stab-badge.goml
@@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 show-text: true
 define-function: (
     "check-badge",
-    (theme, background, color),
+    [theme, background, color],
     block {
         set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}
         go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
diff --git a/tests/rustdoc-gui/target.goml b/tests/rustdoc-gui/target.goml
index 26071df8d044..0f8f77093635 100644
--- a/tests/rustdoc-gui/target.goml
+++ b/tests/rustdoc-gui/target.goml
@@ -7,7 +7,7 @@ assert: "#method\.a_method:target"
 
 define-function: (
     "check-style",
-    (theme, background, border),
+    [theme, background, border],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
diff --git a/tests/rustdoc-gui/toggle-docs.goml b/tests/rustdoc-gui/toggle-docs.goml
index 9ea6d9b18f4c..cfd18bd2e14f 100644
--- a/tests/rustdoc-gui/toggle-docs.goml
+++ b/tests/rustdoc-gui/toggle-docs.goml
@@ -49,7 +49,7 @@ assert-attribute: ("details.toggle", {"open": ""}, ALL)
 show-text: true
 define-function: (
     "check-color",
-    (theme, filter),
+    [theme, filter],
     block {
         // Setting the theme.
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
diff --git a/tests/rustdoc-gui/type-declation-overflow.goml b/tests/rustdoc-gui/type-declation-overflow.goml
index a97cc98897ae..3709aa102661 100644
--- a/tests/rustdoc-gui/type-declation-overflow.goml
+++ b/tests/rustdoc-gui/type-declation-overflow.goml
@@ -47,18 +47,18 @@ assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"})
 // On desktop, they wrap when too big.
 set-window-size: (1100, 800)
 go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
-compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"])
 go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
-compare-elements-position: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+compare-elements-position: (".main-heading h1", ".main-heading .out-of-band", ["y"])
 // make sure there is a gap between them
 compare-elements-position-near-false: (".main-heading h1", ".main-heading .out-of-band", {"x": 550})
 
 // On mobile, they always wrap.
 set-window-size: (600, 600)
 go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
-compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"])
 go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
-compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"])
 
 // Now we will check that the scrolling is working.
 // First on an item with "hidden methods".
diff --git a/tests/rustdoc-gui/unsafe-fn.goml b/tests/rustdoc-gui/unsafe-fn.goml
index 8d26f15f37f1..83503121a046 100644
--- a/tests/rustdoc-gui/unsafe-fn.goml
+++ b/tests/rustdoc-gui/unsafe-fn.goml
@@ -13,7 +13,7 @@ define-function: (
     "sup-check",
     // `theme` is the theme being tested.
     // `color` is the expected color of the `` element.
-    (theme, color),
+    [theme, color],
     block {
         // Set the theme.
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
@@ -23,6 +23,15 @@ define-function: (
     },
 )
 
-call-function: ("sup-check", ("ayu", "#c5c5c5"))
-call-function: ("sup-check", ("dark", "#ddd"))
-call-function: ("sup-check", ("light", "black"))
+call-function: ("sup-check", {
+    "theme": "ayu",
+    "color": "#c5c5c5",
+})
+call-function: ("sup-check", {
+    "theme": "dark",
+    "color": "#ddd",
+})
+call-function: ("sup-check", {
+    "theme": "light",
+    "color": "black",
+})
diff --git a/tests/rustdoc-gui/warning-block.goml b/tests/rustdoc-gui/warning-block.goml
index 10e206049f53..a5a47f868db2 100644
--- a/tests/rustdoc-gui/warning-block.goml
+++ b/tests/rustdoc-gui/warning-block.goml
@@ -5,7 +5,7 @@ show-text: true
 store-value: (default_y_pos, 5)
 define-function: (
     "check-warning",
-    (theme, color, border_color),
+    [theme, color, border_color],
     block {
         set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
         reload:
diff --git a/tests/rustdoc-gui/where-whitespace.goml b/tests/rustdoc-gui/where-whitespace.goml
index da104fa40116..823ce9704076 100644
--- a/tests/rustdoc-gui/where-whitespace.goml
+++ b/tests/rustdoc-gui/where-whitespace.goml
@@ -3,25 +3,25 @@ go-to: "file://" + |DOC_PATH| + "/lib2/trait.Whitespace.html"
 show-text: true
 // First, we check in the trait definition if the where clause is "on its own" (not on the same
 // line than "pub trait Whitespace").
-compare-elements-position-false: (".item-decl code", "div.where", ("y"))
+compare-elements-position-false: (".item-decl code", "div.where", ["y"])
 // And that the code following it isn't on the same line either.
-compare-elements-position-false: (".item-decl .fn", "div.where", ("y"))
+compare-elements-position-false: (".item-decl .fn", "div.where", ["y"])
 
 go-to: "file://" + |DOC_PATH| + "/lib2/struct.WhereWhitespace.html"
 // We make the screen a bit wider to ensure that the trait impl is on one line.
 set-window-size: (915, 915)
 
-compare-elements-position-false: ("#method\.new .fn", "#method\.new div.where", ("y"))
+compare-elements-position-false: ("#method\.new .fn", "#method\.new div.where", ["y"])
 // We ensure that both the trait name and the struct name are on the same line in
 // "impl Whitespace<&K> for WhereWhitespace".
 compare-elements-position: (
     "#trait-implementations-list .impl h3 .trait",
     "#trait-implementations-list .impl h3 .struct",
-    ("y"),
+    ["y"],
 )
 // And we now check that the where condition isn't on the same line.
 compare-elements-position-false: (
     "#trait-implementations-list .impl h3 .trait",
     "#trait-implementations-list .impl h3 div.where",
-    ("y"),
+    ["y"],
 )

From 973663db9dbff6c9fda1ad79884cff5a1675cb06 Mon Sep 17 00:00:00 2001
From: Jubilee Young 
Date: Mon, 1 Apr 2024 13:50:29 -0700
Subject: [PATCH 164/192] Discourage stripping symbols in devtools

---
 src/doc/rustc/src/codegen-options/index.md | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index 5667f0e02ce1..36335abb5253 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -557,8 +557,10 @@ Supported values for this option are:
   using a debugger like gdb or lldb ineffectual.
 - `symbols` - same as `debuginfo`, but the rest of the symbol table section is
   stripped as well if the linker supports it. On platforms which depend on the
-  binary's symbol table for backtraces, this can affect them so negatively as to
-  make the trace completely incomprehensible.
+  binary's symbol table for backtraces, profiling, and similar, this can affect
+  them so negatively as to make the results completely incomprehensible.
+  Programs which may be combined with others, using e.g. CLI pipelines, or any
+  developer tooling whatsoever, should avoid stripping symbols for this reason.
 
 ## symbol-mangling-version
 

From 4994f73a27260e837af98ef906d30d6e512bc52f Mon Sep 17 00:00:00 2001
From: Jubilee Young 
Date: Mon, 1 Apr 2024 14:07:59 -0700
Subject: [PATCH 165/192] Revise strip-symbols paragraph

---
 src/doc/rustc/src/codegen-options/index.md | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index 36335abb5253..0f96f6cb2bc0 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -555,12 +555,11 @@ Supported values for this option are:
   section are stripped at link time and are not copied to the produced binary
   or separate files. This should leave backtraces mostly-intact but may make
   using a debugger like gdb or lldb ineffectual.
-- `symbols` - same as `debuginfo`, but the rest of the symbol table section is
-  stripped as well if the linker supports it. On platforms which depend on the
-  binary's symbol table for backtraces, profiling, and similar, this can affect
-  them so negatively as to make the results completely incomprehensible.
-  Programs which may be combined with others, using e.g. CLI pipelines, or any
-  developer tooling whatsoever, should avoid stripping symbols for this reason.
+- `symbols` - same as `debuginfo`, but the rest of the symbol table section is stripped as well,
+  depending on platform support. On platforms which depend on this symbol table for backtraces,
+  profiling, and similar, this can affect them so negatively as to make the trace incomprehensible.
+  Programs which may be combined with others, such as CLI pipelines and developer tooling,
+  or even anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`.
 
 ## symbol-mangling-version
 

From 1dcaf70c0e114aabb116114dac521f1014204527 Mon Sep 17 00:00:00 2001
From: Jubilee Young 
Date: Mon, 1 Apr 2024 14:13:56 -0700
Subject: [PATCH 166/192] Note -Cstrip is not a security measure

---
 src/doc/rustc/src/codegen-options/index.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index 0f96f6cb2bc0..c8f5d6495703 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -561,6 +561,10 @@ Supported values for this option are:
   Programs which may be combined with others, such as CLI pipelines and developer tooling,
   or even anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`.
 
+Note that, at any level, removing debuginfo only necessarily impacts "friendly" introspection.
+`-Cstrip` cannot be relied on as a meaningful security or obfuscation measure, as disassemblers
+and decompilers can extract considerable information even in the absence of symbols.
+
 ## symbol-mangling-version
 
 This option controls the [name mangling] format for encoding Rust item names

From ca36fe310e8970a66461ff7fc8496a76c75b70ed Mon Sep 17 00:00:00 2001
From: David Carlier 
Date: Mon, 1 Apr 2024 14:28:37 +0100
Subject: [PATCH 167/192] std::thread: set_name change for solaris/illumos.

truncate down to 32 (31 + 1) for solaris/illumos.
---
 library/std/src/sys/pal/unix/thread.rs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 77e31d802a38..5ac68050b95e 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -182,8 +182,11 @@ impl Thread {
 
         if let Some(f) = pthread_setname_np.get() {
             #[cfg(target_os = "nto")]
-            let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name);
+            const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize;
+            #[cfg(any(target_os = "solaris", target_os = "illumos"))]
+            const THREAD_NAME_MAX: usize = 32;
 
+            let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name);
             let res = unsafe { f(libc::pthread_self(), name.as_ptr()) };
             debug_assert_eq!(res, 0);
         }
@@ -360,6 +363,8 @@ impl Drop for Thread {
     target_os = "tvos",
     target_os = "watchos",
     target_os = "nto",
+    target_os = "solaris",
+    target_os = "illumos",
 ))]
 fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] {
     let mut result = [0; MAX_WITH_NUL];

From 069e7f2a768874536e8688e3df120cd74cd03b0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= 
Date: Sun, 31 Mar 2024 20:37:35 +0200
Subject: [PATCH 168/192] rustdoc: heavily simplify synthesis of auto trait
 impls

---
 .../src/traits/auto_trait.rs                  |  15 +-
 src/librustdoc/clean/auto_trait.rs            | 999 ++++++------------
 src/librustdoc/clean/mod.rs                   | 108 +-
 src/librustdoc/clean/simplify.rs              |  45 +-
 src/librustdoc/clean/types.rs                 |   7 -
 src/librustdoc/clean/utils.rs                 |  16 +-
 tests/rustdoc/synthetic_auto/bounds.rs        |  21 +
 tests/rustdoc/synthetic_auto/complex.rs       |   4 +-
 8 files changed, 456 insertions(+), 759 deletions(-)
 create mode 100644 tests/rustdoc/synthetic_auto/bounds.rs

diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 7603c9ed7a86..73e94da165fb 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -25,8 +25,8 @@ pub enum RegionTarget<'tcx> {
 
 #[derive(Default, Debug, Clone)]
 pub struct RegionDeps<'tcx> {
-    larger: FxIndexSet>,
-    smaller: FxIndexSet>,
+    pub larger: FxIndexSet>,
+    pub smaller: FxIndexSet>,
 }
 
 pub enum AutoTraitResult {
@@ -81,19 +81,12 @@ impl<'tcx> AutoTraitFinder<'tcx> {
 
         let infcx = tcx.infer_ctxt().build();
         let mut selcx = SelectionContext::new(&infcx);
-        for polarity in [true, false] {
+        for polarity in [ty::PredicatePolarity::Positive, ty::PredicatePolarity::Negative] {
             let result = selcx.select(&Obligation::new(
                 tcx,
                 ObligationCause::dummy(),
                 orig_env,
-                ty::TraitPredicate {
-                    trait_ref,
-                    polarity: if polarity {
-                        ty::PredicatePolarity::Positive
-                    } else {
-                        ty::PredicatePolarity::Negative
-                    },
-                },
+                ty::TraitPredicate { trait_ref, polarity },
             ));
             if let Ok(Some(ImplSource::UserDefined(_))) = result {
                 debug!(
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index acac686a6fca..08c186c9d38a 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -1,668 +1,361 @@
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
 use rustc_hir as hir;
-use rustc_hir::lang_items::LangItem;
-use rustc_middle::ty::{Region, RegionVid, TypeFoldable};
-use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult};
+use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
+use rustc_middle::bug;
+use rustc_middle::ty::{self, Region, Ty};
+use rustc_span::def_id::DefId;
+use rustc_span::symbol::{kw, Symbol};
+use rustc_trait_selection::traits::auto_trait::{self, RegionTarget};
 
-use std::fmt::Debug;
+use thin_vec::ThinVec;
 
-use super::*;
+use crate::clean::{self, simplify, Lifetime};
+use crate::clean::{
+    clean_generic_param_def, clean_middle_ty, clean_predicate, clean_trait_ref_with_bindings,
+    clean_ty_generics,
+};
+use crate::core::DocContext;
 
-#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
-enum RegionTarget<'tcx> {
-    Region(Region<'tcx>),
-    RegionVid(RegionVid),
-}
+#[instrument(level = "debug", skip(cx))]
+pub(crate) fn synthesize_auto_trait_impls<'tcx>(
+    cx: &mut DocContext<'tcx>,
+    item_def_id: DefId,
+) -> Vec {
+    let tcx = cx.tcx;
+    let param_env = tcx.param_env(item_def_id);
+    let ty = tcx.type_of(item_def_id).instantiate_identity();
 
-#[derive(Default, Debug, Clone)]
-struct RegionDeps<'tcx> {
-    larger: FxIndexSet>,
-    smaller: FxIndexSet>,
-}
-
-pub(crate) struct AutoTraitFinder<'a, 'tcx> {
-    pub(crate) cx: &'a mut core::DocContext<'tcx>,
-}
-
-impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
-    pub(crate) fn new(cx: &'a mut core::DocContext<'tcx>) -> Self {
-        AutoTraitFinder { cx }
-    }
-
-    fn generate_for_trait(
-        &mut self,
-        ty: Ty<'tcx>,
-        trait_def_id: DefId,
-        param_env: ty::ParamEnv<'tcx>,
-        item_def_id: DefId,
-        f: &auto_trait::AutoTraitFinder<'tcx>,
-        // If this is set, show only negative trait implementations, not positive ones.
-        discard_positive_impl: bool,
-    ) -> Option {
-        let tcx = self.cx.tcx;
-        let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty]));
-        if !self.cx.generated_synthetics.insert((ty, trait_def_id)) {
-            debug!("get_auto_trait_impl_for({trait_ref:?}): already generated, aborting");
-            return None;
-        }
-
-        let result = f.find_auto_trait_generics(ty, param_env, trait_def_id, |info| {
-            let region_data = info.region_data;
-
-            let names_map = tcx
-                .generics_of(item_def_id)
-                .params
-                .iter()
-                .filter_map(|param| match param.kind {
-                    ty::GenericParamDefKind::Lifetime => Some(param.name),
-                    _ => None,
-                })
-                .map(|name| (name, Lifetime(name)))
-                .collect();
-            let lifetime_predicates = Self::handle_lifetimes(®ion_data, &names_map);
-            let new_generics = self.param_env_to_generics(
+    let finder = auto_trait::AutoTraitFinder::new(tcx);
+    let mut auto_trait_impls: Vec<_> = cx
+        .auto_traits
+        .clone()
+        .into_iter()
+        .filter_map(|trait_def_id| {
+            synthesize_auto_trait_impl(
+                cx,
+                ty,
+                trait_def_id,
+                param_env,
                 item_def_id,
-                info.full_user_env,
-                lifetime_predicates,
-                info.vid_to_region,
-            );
-
-            debug!(
-                "find_auto_trait_generics(item_def_id={:?}, trait_def_id={:?}): \
-                    finished with {:?}",
-                item_def_id, trait_def_id, new_generics
-            );
-
-            new_generics
-        });
-
-        let polarity;
-        let new_generics = match result {
-            AutoTraitResult::PositiveImpl(new_generics) => {
-                polarity = ty::ImplPolarity::Positive;
-                if discard_positive_impl {
-                    return None;
-                }
-                new_generics
-            }
-            AutoTraitResult::NegativeImpl => {
-                polarity = ty::ImplPolarity::Negative;
-
-                // For negative impls, we use the generic params, but *not* the predicates,
-                // from the original type. Otherwise, the displayed impl appears to be a
-                // conditional negative impl, when it's really unconditional.
-                //
-                // For example, consider the struct Foo(*mut T). Using
-                // the original predicates in our impl would cause us to generate
-                // `impl !Send for Foo`, which makes it appear that Foo
-                // implements Send where T is not copy.
-                //
-                // Instead, we generate `impl !Send for Foo`, which better
-                // expresses the fact that `Foo` never implements `Send`,
-                // regardless of the choice of `T`.
-                let raw_generics = clean_ty_generics(
-                    self.cx,
-                    tcx.generics_of(item_def_id),
-                    ty::GenericPredicates::default(),
-                );
-                let params = raw_generics.params;
-
-                Generics { params, where_predicates: ThinVec::new() }
-            }
-            AutoTraitResult::ExplicitImpl => return None,
-        };
-
-        Some(Item {
-            name: None,
-            attrs: Default::default(),
-            item_id: ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
-            kind: Box::new(ImplItem(Box::new(Impl {
-                unsafety: hir::Unsafety::Normal,
-                generics: new_generics,
-                trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())),
-                for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None, None),
-                items: Vec::new(),
-                polarity,
-                kind: ImplKind::Auto,
-            }))),
-            cfg: None,
-            inline_stmt_id: None,
+                &finder,
+                DiscardPositiveImpls::No,
+            )
         })
+        .collect();
+    // We are only interested in case the type *doesn't* implement the `Sized` trait.
+    if !ty.is_sized(tcx, param_env)
+        && let Some(sized_trait_def_id) = tcx.lang_items().sized_trait()
+        && let Some(impl_item) = synthesize_auto_trait_impl(
+            cx,
+            ty,
+            sized_trait_def_id,
+            param_env,
+            item_def_id,
+            &finder,
+            DiscardPositiveImpls::Yes,
+        )
+    {
+        auto_trait_impls.push(impl_item);
     }
-
-    pub(crate) fn get_auto_trait_impls(&mut self, item_def_id: DefId) -> Vec {
-        let tcx = self.cx.tcx;
-        let param_env = tcx.param_env(item_def_id);
-        let ty = tcx.type_of(item_def_id).instantiate_identity();
-        let f = auto_trait::AutoTraitFinder::new(tcx);
-
-        debug!("get_auto_trait_impls({ty:?})");
-        let auto_traits: Vec<_> = self.cx.auto_traits.to_vec();
-        let mut auto_traits: Vec = auto_traits
-            .into_iter()
-            .filter_map(|trait_def_id| {
-                self.generate_for_trait(ty, trait_def_id, param_env, item_def_id, &f, false)
-            })
-            .collect();
-        // We are only interested in case the type *doesn't* implement the Sized trait.
-        if !ty.is_sized(tcx, param_env) {
-            // In case `#![no_core]` is used, `sized_trait` returns nothing.
-            if let Some(item) = tcx.lang_items().sized_trait().and_then(|sized_trait_did| {
-                self.generate_for_trait(ty, sized_trait_did, param_env, item_def_id, &f, true)
-            }) {
-                auto_traits.push(item);
-            }
-        }
-        auto_traits
-    }
-
-    fn get_lifetime(region: Region<'_>, names_map: &FxIndexMap) -> Lifetime {
-        region_name(region)
-            .map(|name| {
-                names_map
-                    .get(&name)
-                    .unwrap_or_else(|| panic!("Missing lifetime with name {name:?} for {region:?}"))
-            })
-            .unwrap_or(&Lifetime::statik())
-            .clone()
-    }
-
-    /// This method calculates two things: Lifetime constraints of the form `'a: 'b`,
-    /// and region constraints of the form `RegionVid: 'a`
-    ///
-    /// This is essentially a simplified version of lexical_region_resolve. However,
-    /// handle_lifetimes determines what *needs be* true in order for an impl to hold.
-    /// lexical_region_resolve, along with much of the rest of the compiler, is concerned
-    /// with determining if a given set up constraints/predicates *are* met, given some
-    /// starting conditions (e.g., user-provided code). For this reason, it's easier
-    /// to perform the calculations we need on our own, rather than trying to make
-    /// existing inference/solver code do what we want.
-    fn handle_lifetimes<'cx>(
-        regions: &RegionConstraintData<'cx>,
-        names_map: &FxIndexMap,
-    ) -> ThinVec {
-        // Our goal is to 'flatten' the list of constraints by eliminating
-        // all intermediate RegionVids. At the end, all constraints should
-        // be between Regions (aka region variables). This gives us the information
-        // we need to create the Generics.
-        let mut finished: FxIndexMap<_, Vec<_>> = Default::default();
-
-        let mut vid_map: FxIndexMap, RegionDeps<'_>> = Default::default();
-
-        // Flattening is done in two parts. First, we insert all of the constraints
-        // into a map. Each RegionTarget (either a RegionVid or a Region) maps
-        // to its smaller and larger regions. Note that 'larger' regions correspond
-        // to sub-regions in Rust code (e.g., in 'a: 'b, 'a is the larger region).
-        for (constraint, _) in ®ions.constraints {
-            match *constraint {
-                Constraint::VarSubVar(r1, r2) => {
-                    {
-                        let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default();
-                        deps1.larger.insert(RegionTarget::RegionVid(r2));
-                    }
-
-                    let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default();
-                    deps2.smaller.insert(RegionTarget::RegionVid(r1));
-                }
-                Constraint::RegSubVar(region, vid) => {
-                    let deps = vid_map.entry(RegionTarget::RegionVid(vid)).or_default();
-                    deps.smaller.insert(RegionTarget::Region(region));
-                }
-                Constraint::VarSubReg(vid, region) => {
-                    let deps = vid_map.entry(RegionTarget::RegionVid(vid)).or_default();
-                    deps.larger.insert(RegionTarget::Region(region));
-                }
-                Constraint::RegSubReg(r1, r2) => {
-                    // The constraint is already in the form that we want, so we're done with it
-                    // Desired order is 'larger, smaller', so flip then
-                    if region_name(r1) != region_name(r2) {
-                        finished
-                            .entry(region_name(r2).expect("no region_name found"))
-                            .or_default()
-                            .push(r1);
-                    }
-                }
-            }
-        }
-
-        // Here, we 'flatten' the map one element at a time.
-        // All of the element's sub and super regions are connected
-        // to each other. For example, if we have a graph that looks like this:
-        //
-        // (A, B) - C - (D, E)
-        // Where (A, B) are subregions, and (D,E) are super-regions
-        //
-        // then after deleting 'C', the graph will look like this:
-        //  ... - A - (D, E ...)
-        //  ... - B - (D, E, ...)
-        //  (A, B, ...) - D - ...
-        //  (A, B, ...) - E - ...
-        //
-        //  where '...' signifies the existing sub and super regions of an entry
-        //  When two adjacent ty::Regions are encountered, we've computed a final
-        //  constraint, and add it to our list. Since we make sure to never re-add
-        //  deleted items, this process will always finish.
-        while !vid_map.is_empty() {
-            let target = *vid_map.keys().next().unwrap();
-            let deps = vid_map.swap_remove(&target).unwrap();
-
-            for smaller in deps.smaller.iter() {
-                for larger in deps.larger.iter() {
-                    match (smaller, larger) {
-                        (&RegionTarget::Region(r1), &RegionTarget::Region(r2)) => {
-                            if region_name(r1) != region_name(r2) {
-                                finished
-                                    .entry(region_name(r2).expect("no region name found"))
-                                    .or_default()
-                                    .push(r1) // Larger, smaller
-                            }
-                        }
-                        (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
-                            if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) {
-                                let smaller_deps = v.into_mut();
-                                smaller_deps.larger.insert(*larger);
-                                smaller_deps.larger.swap_remove(&target);
-                            }
-                        }
-                        (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
-                            if let IndexEntry::Occupied(v) = vid_map.entry(*larger) {
-                                let deps = v.into_mut();
-                                deps.smaller.insert(*smaller);
-                                deps.smaller.swap_remove(&target);
-                            }
-                        }
-                        (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
-                            if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) {
-                                let smaller_deps = v.into_mut();
-                                smaller_deps.larger.insert(*larger);
-                                smaller_deps.larger.swap_remove(&target);
-                            }
-
-                            if let IndexEntry::Occupied(v) = vid_map.entry(*larger) {
-                                let larger_deps = v.into_mut();
-                                larger_deps.smaller.insert(*smaller);
-                                larger_deps.smaller.swap_remove(&target);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        let lifetime_predicates = names_map
-            .iter()
-            .flat_map(|(name, lifetime)| {
-                let empty = Vec::new();
-                let bounds: FxIndexSet = finished
-                    .get(name)
-                    .unwrap_or(&empty)
-                    .iter()
-                    .map(|region| GenericBound::Outlives(Self::get_lifetime(*region, names_map)))
-                    .collect();
-
-                if bounds.is_empty() {
-                    return None;
-                }
-                Some(WherePredicate::RegionPredicate {
-                    lifetime: lifetime.clone(),
-                    bounds: bounds.into_iter().collect(),
-                })
-            })
-            .collect();
-
-        lifetime_predicates
-    }
-
-    fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxIndexSet {
-        let bound_predicate = pred.kind();
-        let tcx = self.cx.tcx;
-        let regions =
-            match bound_predicate.skip_binder() {
-                ty::ClauseKind::Trait(poly_trait_pred) => tcx
-                    .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_trait_pred)),
-                ty::ClauseKind::Projection(poly_proj_pred) => tcx
-                    .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_proj_pred)),
-                _ => return FxIndexSet::default(),
-            };
-
-        regions
-            .into_iter()
-            .filter_map(|br| {
-                match br {
-                    // We only care about named late bound regions, as we need to add them
-                    // to the 'for<>' section
-                    ty::BrNamed(def_id, name) => Some(GenericParamDef::lifetime(def_id, name)),
-                    _ => None,
-                }
-            })
-            .collect()
-    }
-
-    fn make_final_bounds(
-        &self,
-        ty_to_bounds: FxIndexMap>,
-        ty_to_fn: FxIndexMap)>,
-        lifetime_to_bounds: FxIndexMap>,
-    ) -> Vec {
-        ty_to_bounds
-            .into_iter()
-            .flat_map(|(ty, mut bounds)| {
-                if let Some((ref poly_trait, ref output)) = ty_to_fn.get(&ty) {
-                    let mut new_path = poly_trait.trait_.clone();
-                    let last_segment = new_path.segments.pop().expect("segments were empty");
-
-                    let (old_input, old_output) = match last_segment.args {
-                        GenericArgs::AngleBracketed { args, .. } => {
-                            let types = args
-                                .iter()
-                                .filter_map(|arg| match arg {
-                                    GenericArg::Type(ty) => Some(ty.clone()),
-                                    _ => None,
-                                })
-                                .collect();
-                            (types, None)
-                        }
-                        GenericArgs::Parenthesized { inputs, output } => (inputs, output),
-                    };
-
-                    let output = output.as_ref().cloned().map(Box::new);
-                    if old_output.is_some() && old_output != output {
-                        panic!("Output mismatch for {ty:?} {old_output:?} {output:?}");
-                    }
-
-                    let new_params = GenericArgs::Parenthesized { inputs: old_input, output };
-
-                    new_path
-                        .segments
-                        .push(PathSegment { name: last_segment.name, args: new_params });
-
-                    bounds.insert(GenericBound::TraitBound(
-                        PolyTrait {
-                            trait_: new_path,
-                            generic_params: poly_trait.generic_params.clone(),
-                        },
-                        hir::TraitBoundModifier::None,
-                    ));
-                }
-                if bounds.is_empty() {
-                    return None;
-                }
-
-                Some(WherePredicate::BoundPredicate {
-                    ty,
-                    bounds: bounds.into_iter().collect(),
-                    bound_params: Vec::new(),
-                })
-            })
-            .chain(lifetime_to_bounds.into_iter().filter(|(_, bounds)| !bounds.is_empty()).map(
-                |(lifetime, bounds)| WherePredicate::RegionPredicate {
-                    lifetime,
-                    bounds: bounds.into_iter().collect(),
-                },
-            ))
-            .collect()
-    }
-
-    /// Converts the calculated `ParamEnv` and lifetime information to a [`clean::Generics`](Generics), suitable for
-    /// display on the docs page. Cleaning the `Predicates` produces sub-optimal [`WherePredicate`]s,
-    /// so we fix them up:
-    ///
-    /// * Multiple bounds for the same type are coalesced into one: e.g., `T: Copy`, `T: Debug`
-    /// becomes `T: Copy + Debug`
-    /// * `Fn` bounds are handled specially - instead of leaving it as `T: Fn(),  =
-    /// K`, we use the dedicated syntax `T: Fn() -> K`
-    /// * We explicitly add a `?Sized` bound if we didn't find any `Sized` predicates for a type
-    #[instrument(level = "debug", skip(self, vid_to_region))]
-    fn param_env_to_generics(
-        &mut self,
-        item_def_id: DefId,
-        param_env: ty::ParamEnv<'tcx>,
-        mut existing_predicates: ThinVec,
-        vid_to_region: FxIndexMap>,
-    ) -> Generics {
-        let tcx = self.cx.tcx;
-
-        // The `Sized` trait must be handled specially, since we only display it when
-        // it is *not* required (i.e., '?Sized')
-        let sized_trait = tcx.require_lang_item(LangItem::Sized, None);
-
-        let mut replacer = RegionReplacer { vid_to_region: &vid_to_region, tcx };
-
-        // FIXME(fmease): Remove this!
-        let orig_bounds: FxHashSet<_> = tcx.param_env(item_def_id).caller_bounds().iter().collect();
-        let clean_where_predicates = param_env
-            .caller_bounds()
-            .iter()
-            .filter(|p| {
-                !orig_bounds.contains(p)
-                    || match p.kind().skip_binder() {
-                        ty::ClauseKind::Trait(pred) => pred.def_id() == sized_trait,
-                        _ => false,
-                    }
-            })
-            .map(|p| p.fold_with(&mut replacer));
-
-        let raw_generics = clean_ty_generics(
-            self.cx,
-            tcx.generics_of(item_def_id),
-            tcx.explicit_predicates_of(item_def_id),
-        );
-        let mut generic_params = raw_generics.params;
-
-        debug!("param_env_to_generics({item_def_id:?}): generic_params={generic_params:?}");
-
-        let mut has_sized = FxHashSet::default(); // NOTE(fmease): not used for iteration
-        let mut ty_to_bounds = FxIndexMap::<_, FxIndexSet<_>>::default();
-        let mut lifetime_to_bounds = FxIndexMap::<_, FxIndexSet<_>>::default();
-        let mut ty_to_traits = FxIndexMap::>::default();
-        let mut ty_to_fn = FxIndexMap::)>::default();
-
-        // FIXME: This code shares much of the logic found in `clean_ty_generics` and
-        //        `simplify::where_clause`. Consider deduplicating it to avoid diverging
-        //        implementations.
-        //        Further, the code below does not merge (partially re-sugared) bounds like
-        //        `Tr` & `Tr` and it does not render higher-ranked parameters
-        //        originating from equality predicates.
-        for p in clean_where_predicates {
-            let (orig_p, p) = (p, clean_predicate(p, self.cx));
-            if p.is_none() {
-                continue;
-            }
-            let p = p.unwrap();
-            match p {
-                WherePredicate::BoundPredicate { ty, mut bounds, .. } => {
-                    // Writing a projection trait bound of the form
-                    // ::Name : ?Sized
-                    // is illegal, because ?Sized bounds can only
-                    // be written in the (here, nonexistent) definition
-                    // of the type.
-                    // Therefore, we make sure that we never add a ?Sized
-                    // bound for projections
-                    if let Type::QPath { .. } = ty {
-                        has_sized.insert(ty.clone());
-                    }
-
-                    if bounds.is_empty() {
-                        continue;
-                    }
-
-                    let mut for_generics = self.extract_for_generics(orig_p);
-
-                    assert!(bounds.len() == 1);
-                    let mut b = bounds.pop().expect("bounds were empty");
-
-                    if b.is_sized_bound(self.cx) {
-                        has_sized.insert(ty.clone());
-                    } else if !b
-                        .get_trait_path()
-                        .and_then(|trait_| {
-                            ty_to_traits
-                                .get(&ty)
-                                .map(|bounds| bounds.contains(&strip_path_generics(trait_)))
-                        })
-                        .unwrap_or(false)
-                    {
-                        // If we've already added a projection bound for the same type, don't add
-                        // this, as it would be a duplicate
-
-                        // Handle any 'Fn/FnOnce/FnMut' bounds specially,
-                        // as we want to combine them with any 'Output' qpaths
-                        // later
-
-                        let is_fn = match b {
-                            GenericBound::TraitBound(ref mut p, _) => {
-                                // Insert regions into the for_generics hash map first, to ensure
-                                // that we don't end up with duplicate bounds (e.g., for<'b, 'b>)
-                                for_generics.extend(p.generic_params.drain(..));
-                                p.generic_params.extend(for_generics);
-                                tcx.is_fn_trait(p.trait_.def_id())
-                            }
-                            _ => false,
-                        };
-
-                        let poly_trait = b.get_poly_trait().expect("Cannot get poly trait");
-
-                        if is_fn {
-                            ty_to_fn
-                                .entry(ty.clone())
-                                .and_modify(|e| *e = (poly_trait.clone(), e.1.clone()))
-                                .or_insert(((poly_trait.clone()), None));
-
-                            ty_to_bounds.entry(ty.clone()).or_default();
-                        } else {
-                            ty_to_bounds.entry(ty.clone()).or_default().insert(b.clone());
-                        }
-                    }
-                }
-                WherePredicate::RegionPredicate { lifetime, bounds } => {
-                    lifetime_to_bounds.entry(lifetime).or_default().extend(bounds);
-                }
-                WherePredicate::EqPredicate { lhs, rhs } => {
-                    match lhs {
-                        Type::QPath(box QPathData {
-                            ref assoc,
-                            ref self_type,
-                            trait_: Some(ref trait_),
-                            ..
-                        }) => {
-                            let ty = &*self_type;
-                            let mut new_trait = trait_.clone();
-
-                            if tcx.is_fn_trait(trait_.def_id()) && assoc.name == sym::Output {
-                                ty_to_fn
-                                    .entry(ty.clone())
-                                    .and_modify(|e| {
-                                        *e = (e.0.clone(), Some(rhs.ty().unwrap().clone()))
-                                    })
-                                    .or_insert((
-                                        PolyTrait {
-                                            trait_: trait_.clone(),
-                                            generic_params: Vec::new(),
-                                        },
-                                        Some(rhs.ty().unwrap().clone()),
-                                    ));
-                                continue;
-                            }
-
-                            let args = &mut new_trait
-                                .segments
-                                .last_mut()
-                                .expect("segments were empty")
-                                .args;
-
-                            match args {
-                                // Convert something like ' = u8'
-                                // to 'T: Iterator'
-                                GenericArgs::AngleBracketed { ref mut bindings, .. } => {
-                                    bindings.push(TypeBinding {
-                                        assoc: assoc.clone(),
-                                        kind: TypeBindingKind::Equality { term: rhs },
-                                    });
-                                }
-                                GenericArgs::Parenthesized { .. } => {
-                                    existing_predicates.push(WherePredicate::EqPredicate {
-                                        lhs: lhs.clone(),
-                                        rhs,
-                                    });
-                                    continue; // If something other than a Fn ends up
-                                    // with parentheses, leave it alone
-                                }
-                            }
-
-                            let bounds = ty_to_bounds.entry(ty.clone()).or_default();
-
-                            bounds.insert(GenericBound::TraitBound(
-                                PolyTrait { trait_: new_trait, generic_params: Vec::new() },
-                                hir::TraitBoundModifier::None,
-                            ));
-
-                            // Remove any existing 'plain' bound (e.g., 'T: Iterator`) so
-                            // that we don't see a
-                            // duplicate bound like `T: Iterator + Iterator`
-                            // on the docs page.
-                            bounds.swap_remove(&GenericBound::TraitBound(
-                                PolyTrait { trait_: trait_.clone(), generic_params: Vec::new() },
-                                hir::TraitBoundModifier::None,
-                            ));
-                            // Avoid creating any new duplicate bounds later in the outer
-                            // loop
-                            ty_to_traits.entry(ty.clone()).or_default().insert(trait_.clone());
-                        }
-                        _ => panic!("Unexpected LHS {lhs:?} for {item_def_id:?}"),
-                    }
-                }
-            };
-        }
-
-        let final_bounds = self.make_final_bounds(ty_to_bounds, ty_to_fn, lifetime_to_bounds);
-
-        existing_predicates.extend(final_bounds);
-
-        for param in generic_params.iter_mut() {
-            match param.kind {
-                GenericParamDefKind::Type { ref mut default, ref mut bounds, .. } => {
-                    // We never want something like `impl`.
-                    default.take();
-                    let generic_ty = Type::Generic(param.name);
-                    if !has_sized.contains(&generic_ty) {
-                        bounds.insert(0, GenericBound::maybe_sized(self.cx));
-                    }
-                }
-                GenericParamDefKind::Lifetime { .. } => {}
-                GenericParamDefKind::Const { ref mut default, .. } => {
-                    // We never want something like `impl`
-                    default.take();
-                }
-            }
-        }
-
-        Generics { params: generic_params, where_predicates: existing_predicates }
-    }
+    auto_trait_impls
 }
 
-fn region_name(region: Region<'_>) -> Option {
+#[instrument(level = "debug", skip(cx, finder))]
+fn synthesize_auto_trait_impl<'tcx>(
+    cx: &mut DocContext<'tcx>,
+    ty: Ty<'tcx>,
+    trait_def_id: DefId,
+    param_env: ty::ParamEnv<'tcx>,
+    item_def_id: DefId,
+    finder: &auto_trait::AutoTraitFinder<'tcx>,
+    discard_positive_impls: DiscardPositiveImpls,
+) -> Option {
+    let tcx = cx.tcx;
+    let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty]));
+    if !cx.generated_synthetics.insert((ty, trait_def_id)) {
+        debug!("already generated, aborting");
+        return None;
+    }
+
+    let result = finder.find_auto_trait_generics(ty, param_env, trait_def_id, |info| {
+        clean_param_env(cx, item_def_id, info.full_user_env, info.region_data, info.vid_to_region)
+    });
+
+    let (generics, polarity) = match result {
+        auto_trait::AutoTraitResult::PositiveImpl(generics) => {
+            if let DiscardPositiveImpls::Yes = discard_positive_impls {
+                return None;
+            }
+
+            (generics, ty::ImplPolarity::Positive)
+        }
+        auto_trait::AutoTraitResult::NegativeImpl => {
+            // For negative impls, we use the generic params, but *not* the predicates,
+            // from the original type. Otherwise, the displayed impl appears to be a
+            // conditional negative impl, when it's really unconditional.
+            //
+            // For example, consider the struct Foo(*mut T). Using
+            // the original predicates in our impl would cause us to generate
+            // `impl !Send for Foo`, which makes it appear that Foo
+            // implements Send where T is not copy.
+            //
+            // Instead, we generate `impl !Send for Foo`, which better
+            // expresses the fact that `Foo` never implements `Send`,
+            // regardless of the choice of `T`.
+            let mut generics = clean_ty_generics(
+                cx,
+                tcx.generics_of(item_def_id),
+                ty::GenericPredicates::default(),
+            );
+            generics.where_predicates.clear();
+
+            (generics, ty::ImplPolarity::Negative)
+        }
+        auto_trait::AutoTraitResult::ExplicitImpl => return None,
+    };
+
+    Some(clean::Item {
+        name: None,
+        attrs: Default::default(),
+        item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
+        kind: Box::new(clean::ImplItem(Box::new(clean::Impl {
+            unsafety: hir::Unsafety::Normal,
+            generics,
+            trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref, ThinVec::new())),
+            for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None),
+            items: Vec::new(),
+            polarity,
+            kind: clean::ImplKind::Auto,
+        }))),
+        cfg: None,
+        inline_stmt_id: None,
+    })
+}
+
+#[derive(Debug)]
+enum DiscardPositiveImpls {
+    Yes,
+    No,
+}
+
+#[instrument(level = "debug", skip(cx, region_data, vid_to_region))]
+fn clean_param_env<'tcx>(
+    cx: &mut DocContext<'tcx>,
+    item_def_id: DefId,
+    param_env: ty::ParamEnv<'tcx>,
+    region_data: RegionConstraintData<'tcx>,
+    vid_to_region: FxIndexMap>,
+) -> clean::Generics {
+    let tcx = cx.tcx;
+    let generics = tcx.generics_of(item_def_id);
+
+    let params: ThinVec<_> = generics
+        .params
+        .iter()
+        .inspect(|param| {
+            if cfg!(debug_assertions) {
+                debug_assert!(!param.is_anonymous_lifetime() && !param.is_host_effect());
+                if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind {
+                    debug_assert!(!synthetic && param.name != kw::SelfUpper);
+                }
+            }
+        })
+        // We're basing the generics of the synthetic auto trait impl off of the generics of the
+        // implementing type. Its generic parameters may have defaults, don't copy them over:
+        // Generic parameter defaults are meaningless in impls.
+        .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx))
+        .collect();
+
+    // FIXME(#111101): Incorporate the explicit predicates of the item here...
+    let item_predicates: FxIndexSet<_> =
+        tcx.predicates_of(item_def_id).predicates.iter().map(|(pred, _)| pred).collect();
+    let where_predicates = param_env
+        .caller_bounds()
+        .iter()
+        // FIXME: ...which hopefully allows us to simplify this:
+        .filter(|pred| {
+            !item_predicates.contains(pred)
+                || pred
+                    .as_trait_clause()
+                    .is_some_and(|pred| tcx.lang_items().sized_trait() == Some(pred.def_id()))
+        })
+        .map(|pred| {
+            tcx.fold_regions(pred, |r, _| match *r {
+                ty::ReVar(vid) => vid_to_region[&vid],
+                ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r,
+                ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => {
+                    bug!("unexpected region kind: {r:?}")
+                }
+            })
+        })
+        .flat_map(|pred| clean_predicate(pred, cx))
+        .chain(clean_region_outlives_constraints(®ion_data, generics))
+        .collect();
+
+    let mut generics = clean::Generics { params, where_predicates };
+    simplify::sized_bounds(cx, &mut generics);
+    generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates);
+    generics
+}
+
+/// Clean region outlives constraints to where-predicates.
+///
+/// This is essentially a simplified version of `lexical_region_resolve`.
+///
+/// However, here we determine what *needs to be* true in order for an impl to hold.
+/// `lexical_region_resolve`, along with much of the rest of the compiler, is concerned
+/// with determining if a given set up constraints / predicates *are* met, given some
+/// starting conditions like user-provided code.
+///
+/// For this reason, it's easier to perform the calculations we need on our own,
+/// rather than trying to make existing inference/solver code do what we want.
+fn clean_region_outlives_constraints<'tcx>(
+    regions: &RegionConstraintData<'tcx>,
+    generics: &'tcx ty::Generics,
+) -> ThinVec {
+    // Our goal is to "flatten" the list of constraints by eliminating all intermediate
+    // `RegionVids` (region inference variables). At the end, all constraints should be
+    // between `Region`s. This gives us the information we need to create the where-predicates.
+    // This flattening is done in two parts.
+
+    let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default();
+    let mut map = FxIndexMap::, auto_trait::RegionDeps<'_>>::default();
+
+    // (1)  We insert all of the constraints into a map.
+    // Each `RegionTarget` (a `RegionVid` or a `Region`) maps to its smaller and larger regions.
+    // Note that "larger" regions correspond to sub regions in the surface language.
+    // E.g., in `'a: 'b`, `'a` is the larger region.
+    for (constraint, _) in ®ions.constraints {
+        match *constraint {
+            Constraint::VarSubVar(vid1, vid2) => {
+                let deps1 = map.entry(RegionTarget::RegionVid(vid1)).or_default();
+                deps1.larger.insert(RegionTarget::RegionVid(vid2));
+
+                let deps2 = map.entry(RegionTarget::RegionVid(vid2)).or_default();
+                deps2.smaller.insert(RegionTarget::RegionVid(vid1));
+            }
+            Constraint::RegSubVar(region, vid) => {
+                let deps = map.entry(RegionTarget::RegionVid(vid)).or_default();
+                deps.smaller.insert(RegionTarget::Region(region));
+            }
+            Constraint::VarSubReg(vid, region) => {
+                let deps = map.entry(RegionTarget::RegionVid(vid)).or_default();
+                deps.larger.insert(RegionTarget::Region(region));
+            }
+            Constraint::RegSubReg(r1, r2) => {
+                // The constraint is already in the form that we want, so we're done with it
+                // The desired order is [larger, smaller], so flip them.
+                if early_bound_region_name(r1) != early_bound_region_name(r2) {
+                    outlives_predicates
+                        .entry(early_bound_region_name(r2).expect("no region_name found"))
+                        .or_default()
+                        .push(r1);
+                }
+            }
+        }
+    }
+
+    // (2)  Here, we "flatten" the map one element at a time. All of the elements' sub and super
+    // regions are connected to each other. For example, if we have a graph that looks like this:
+    //
+    //     (A, B) - C - (D, E)
+    //
+    // where (A, B) are sub regions, and (D,E) are super regions.
+    // Then, after deleting 'C', the graph will look like this:
+    //
+    //             ... - A - (D, E, ...)
+    //             ... - B - (D, E, ...)
+    //     (A, B, ...) - D - ...
+    //     (A, B, ...) - E - ...
+    //
+    // where '...' signifies the existing sub and super regions of an entry. When two adjacent
+    // `Region`s are encountered, we've computed a final constraint, and add it to our list.
+    // Since we make sure to never re-add deleted items, this process will always finish.
+    while !map.is_empty() {
+        let target = *map.keys().next().unwrap();
+        let deps = map.swap_remove(&target).unwrap();
+
+        for smaller in &deps.smaller {
+            for larger in &deps.larger {
+                match (smaller, larger) {
+                    (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => {
+                        if early_bound_region_name(smaller) != early_bound_region_name(larger) {
+                            outlives_predicates
+                                .entry(
+                                    early_bound_region_name(larger).expect("no region name found"),
+                                )
+                                .or_default()
+                                .push(smaller)
+                        }
+                    }
+                    (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
+                        if let IndexEntry::Occupied(v) = map.entry(*smaller) {
+                            let smaller_deps = v.into_mut();
+                            smaller_deps.larger.insert(*larger);
+                            smaller_deps.larger.swap_remove(&target);
+                        }
+                    }
+                    (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
+                        if let IndexEntry::Occupied(v) = map.entry(*larger) {
+                            let deps = v.into_mut();
+                            deps.smaller.insert(*smaller);
+                            deps.smaller.swap_remove(&target);
+                        }
+                    }
+                    (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
+                        if let IndexEntry::Occupied(v) = map.entry(*smaller) {
+                            let smaller_deps = v.into_mut();
+                            smaller_deps.larger.insert(*larger);
+                            smaller_deps.larger.swap_remove(&target);
+                        }
+                        if let IndexEntry::Occupied(v) = map.entry(*larger) {
+                            let larger_deps = v.into_mut();
+                            larger_deps.smaller.insert(*smaller);
+                            larger_deps.smaller.swap_remove(&target);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    let region_params: FxIndexSet<_> = generics
+        .params
+        .iter()
+        .filter_map(|param| match param.kind {
+            ty::GenericParamDefKind::Lifetime => Some(param.name),
+            _ => None,
+        })
+        .collect();
+
+    region_params
+        .iter()
+        .filter_map(|&name| {
+            let bounds: FxIndexSet<_> = outlives_predicates
+                .get(&name)?
+                .iter()
+                .map(|®ion| {
+                    let lifetime = early_bound_region_name(region)
+                        .inspect(|name| assert!(region_params.contains(name)))
+                        .map(|name| Lifetime(name))
+                        .unwrap_or(Lifetime::statik());
+                    clean::GenericBound::Outlives(lifetime)
+                })
+                .collect();
+            if bounds.is_empty() {
+                return None;
+            }
+            Some(clean::WherePredicate::RegionPredicate {
+                lifetime: Lifetime(name),
+                bounds: bounds.into_iter().collect(),
+            })
+        })
+        .collect()
+}
+
+fn early_bound_region_name(region: Region<'_>) -> Option {
     match *region {
         ty::ReEarlyParam(r) => Some(r.name),
         _ => None,
     }
 }
-
-/// Replaces all [`ty::RegionVid`]s in a type with [`ty::Region`]s, using the provided map.
-struct RegionReplacer<'a, 'tcx> {
-    vid_to_region: &'a FxIndexMap>,
-    tcx: TyCtxt<'tcx>,
-}
-
-impl<'a, 'tcx> TypeFolder> for RegionReplacer<'a, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        match *r {
-            // These are the regions that can be seen in the AST.
-            ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned().unwrap_or(r),
-            ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r,
-            r => bug!("unexpected region: {r:?}"),
-        }
-    }
-}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 9023d9d41179..a25a506d9c5b 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -21,10 +21,8 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE};
 use rustc_hir::PredicateOrigin;
 use rustc_hir_analysis::lower_ty;
-use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
 use rustc_middle::metadata::Reexport;
 use rustc_middle::middle::resolve_bound_vars as rbv;
-use rustc_middle::ty::fold::TypeFolder;
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::TypeVisitableExt;
 use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt};
@@ -36,7 +34,6 @@ use rustc_trait_selection::traits::wf::object_region_bounds;
 
 use std::borrow::Cow;
 use std::collections::BTreeMap;
-use std::hash::Hash;
 use std::mem;
 use thin_vec::ThinVec;
 
@@ -501,6 +498,7 @@ fn projection_to_path_segment<'tcx>(
 
 fn clean_generic_param_def<'tcx>(
     def: &ty::GenericParamDef,
+    defaults: ParamDefaults,
     cx: &mut DocContext<'tcx>,
 ) -> GenericParamDef {
     let (name, kind) = match def.kind {
@@ -508,7 +506,9 @@ fn clean_generic_param_def<'tcx>(
             (def.name, GenericParamDefKind::Lifetime { outlives: ThinVec::new() })
         }
         ty::GenericParamDefKind::Type { has_default, synthetic, .. } => {
-            let default = if has_default {
+            let default = if let ParamDefaults::Yes = defaults
+                && has_default
+            {
                 Some(clean_middle_ty(
                     ty::Binder::dummy(cx.tcx.type_of(def.def_id).instantiate_identity()),
                     cx,
@@ -541,11 +541,14 @@ fn clean_generic_param_def<'tcx>(
                     Some(def.def_id),
                     None,
                 )),
-                default: match has_default {
-                    true => Some(Box::new(
+                default: if let ParamDefaults::Yes = defaults
+                    && has_default
+                {
+                    Some(Box::new(
                         cx.tcx.const_param_default(def.def_id).instantiate_identity().to_string(),
-                    )),
-                    false => None,
+                    ))
+                } else {
+                    None
                 },
                 is_host_effect,
             },
@@ -555,6 +558,12 @@ fn clean_generic_param_def<'tcx>(
     GenericParamDef { name, def_id: def.def_id, kind }
 }
 
+/// Whether to clean generic parameter defaults or not.
+enum ParamDefaults {
+    Yes,
+    No,
+}
+
 fn clean_generic_param<'tcx>(
     cx: &mut DocContext<'tcx>,
     generics: Option<&hir::Generics<'tcx>>,
@@ -758,34 +767,30 @@ fn clean_ty_generics<'tcx>(
     gens: &ty::Generics,
     preds: ty::GenericPredicates<'tcx>,
 ) -> Generics {
-    // Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses,
-    // since `Clean for ty::Predicate` would consume them.
+    // Don't populate `cx.impl_trait_bounds` before cleaning where clauses,
+    // since `clean_predicate` would consume them.
     let mut impl_trait = BTreeMap::>::default();
 
-    // Bounds in the type_params and lifetimes fields are repeated in the
-    // predicates field (see rustc_hir_analysis::collect::ty_generics), so remove
-    // them.
-    let stripped_params = gens
+    let params: ThinVec<_> = gens
         .params
         .iter()
-        .filter_map(|param| match param.kind {
-            ty::GenericParamDefKind::Lifetime if param.is_anonymous_lifetime() => None,
-            ty::GenericParamDefKind::Lifetime => Some(clean_generic_param_def(param, cx)),
+        .filter(|param| match param.kind {
+            ty::GenericParamDefKind::Lifetime => !param.is_anonymous_lifetime(),
             ty::GenericParamDefKind::Type { synthetic, .. } => {
                 if param.name == kw::SelfUpper {
-                    assert_eq!(param.index, 0);
-                    return None;
+                    debug_assert_eq!(param.index, 0);
+                    return false;
                 }
                 if synthetic {
                     impl_trait.insert(param.index, vec![]);
-                    return None;
+                    return false;
                 }
-                Some(clean_generic_param_def(param, cx))
+                true
             }
-            ty::GenericParamDefKind::Const { is_host_effect: true, .. } => None,
-            ty::GenericParamDefKind::Const { .. } => Some(clean_generic_param_def(param, cx)),
+            ty::GenericParamDefKind::Const { is_host_effect, .. } => !is_host_effect,
         })
-        .collect::>();
+        .map(|param| clean_generic_param_def(param, ParamDefaults::Yes, cx))
+        .collect();
 
     // param index -> [(trait DefId, associated type name & generics, term)]
     let mut impl_trait_proj =
@@ -881,56 +886,13 @@ fn clean_ty_generics<'tcx>(
 
     // Now that `cx.impl_trait_bounds` is populated, we can process
     // remaining predicates which could contain `impl Trait`.
-    let mut where_predicates =
-        where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect::>();
+    let where_predicates =
+        where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect();
 
-    // In the surface language, all type parameters except `Self` have an
-    // implicit `Sized` bound unless removed with `?Sized`.
-    // However, in the list of where-predicates below, `Sized` appears like a
-    // normal bound: It's either present (the type is sized) or
-    // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`).
-    //
-    // This is unsuitable for rendering.
-    // Thus, as a first step remove all `Sized` bounds that should be implicit.
-    //
-    // Note that associated types also have an implicit `Sized` bound but we
-    // don't actually know the set of associated types right here so that's
-    // handled when cleaning associated types.
-    let mut sized_params = FxHashSet::default();
-    where_predicates.retain(|pred| {
-        if let WherePredicate::BoundPredicate { ty: Generic(g), bounds, .. } = pred
-            && *g != kw::SelfUpper
-            && bounds.iter().any(|b| b.is_sized_bound(cx))
-        {
-            sized_params.insert(*g);
-            false
-        } else {
-            true
-        }
-    });
-
-    // As a final step, go through the type parameters again and insert a
-    // `?Sized` bound for each one we didn't find to be `Sized`.
-    for tp in &stripped_params {
-        if let types::GenericParamDefKind::Type { .. } = tp.kind
-            && !sized_params.contains(&tp.name)
-        {
-            where_predicates.push(WherePredicate::BoundPredicate {
-                ty: Type::Generic(tp.name),
-                bounds: vec![GenericBound::maybe_sized(cx)],
-                bound_params: Vec::new(),
-            })
-        }
-    }
-
-    // It would be nice to collect all of the bounds on a type and recombine
-    // them if possible, to avoid e.g., `where T: Foo, T: Bar, T: Sized, T: 'a`
-    // and instead see `where T: Foo + Bar + Sized + 'a`
-
-    Generics {
-        params: stripped_params,
-        where_predicates: simplify::where_clauses(cx, where_predicates),
-    }
+    let mut generics = Generics { params, where_predicates };
+    simplify::sized_bounds(cx, &mut generics);
+    generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates);
+    generics
 }
 
 fn clean_ty_alias_inner_type<'tcx>(
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index c35fb9ec7887..5a3ccb6239a0 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -12,6 +12,7 @@
 //! bounds by special casing scenarios such as these. Fun!
 
 use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::unord::UnordSet;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty;
 use thin_vec::ThinVec;
@@ -21,7 +22,7 @@ use crate::clean::GenericArgs as PP;
 use crate::clean::WherePredicate as WP;
 use crate::core::DocContext;
 
-pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> ThinVec {
+pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: ThinVec) -> ThinVec {
     // First, partition the where clause into its separate components.
     //
     // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to
@@ -128,6 +129,48 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId)
         .any(|did| trait_is_same_or_supertrait(cx, did, trait_))
 }
 
+pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generics) {
+    let mut sized_params = UnordSet::new();
+
+    // In the surface language, all type parameters except `Self` have an
+    // implicit `Sized` bound unless removed with `?Sized`.
+    // However, in the list of where-predicates below, `Sized` appears like a
+    // normal bound: It's either present (the type is sized) or
+    // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`).
+    //
+    // This is unsuitable for rendering.
+    // Thus, as a first step remove all `Sized` bounds that should be implicit.
+    //
+    // Note that associated types also have an implicit `Sized` bound but we
+    // don't actually know the set of associated types right here so that
+    // should be handled when cleaning associated types.
+    generics.where_predicates.retain(|pred| {
+        if let WP::BoundPredicate { ty: clean::Generic(param), bounds, .. } = pred
+            && *param != rustc_span::symbol::kw::SelfUpper
+            && bounds.iter().any(|b| b.is_sized_bound(cx))
+        {
+            sized_params.insert(*param);
+            false
+        } else {
+            true
+        }
+    });
+
+    // As a final step, go through the type parameters again and insert a
+    // `?Sized` bound for each one we didn't find to be `Sized`.
+    for param in &generics.params {
+        if let clean::GenericParamDefKind::Type { .. } = param.kind
+            && !sized_params.contains(¶m.name)
+        {
+            generics.where_predicates.push(WP::BoundPredicate {
+                ty: clean::Type::Generic(param.name),
+                bounds: vec![clean::GenericBound::maybe_sized(cx)],
+                bound_params: Vec::new(),
+            })
+        }
+    }
+}
+
 /// Move bounds that are (likely) directly attached to generic parameters from the where-clause to
 /// the respective parameter.
 ///
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index a51f6360df2a..6793ea9f485f 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1277,13 +1277,6 @@ impl GenericBound {
         false
     }
 
-    pub(crate) fn get_poly_trait(&self) -> Option {
-        if let GenericBound::TraitBound(ref p, _) = *self {
-            return Some(p.clone());
-        }
-        None
-    }
-
     pub(crate) fn get_trait_path(&self) -> Option {
         if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self {
             Some(trait_.clone())
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 977b4bb45b62..d5e0e83696f3 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -1,4 +1,4 @@
-use crate::clean::auto_trait::AutoTraitFinder;
+use crate::clean::auto_trait::synthesize_auto_trait_impls;
 use crate::clean::blanket_impl::BlanketImplFinder;
 use crate::clean::render_macro_matchers::render_macro_matcher;
 use crate::clean::{
@@ -251,15 +251,6 @@ pub(super) fn clean_middle_path<'tcx>(
     }
 }
 
-/// Remove the generic arguments from a path.
-pub(crate) fn strip_path_generics(mut path: Path) -> Path {
-    for ps in path.segments.iter_mut() {
-        ps.args = GenericArgs::AngleBracketed { args: Default::default(), bindings: ThinVec::new() }
-    }
-
-    path
-}
-
 pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String {
     let segments = match *p {
         hir::QPath::Resolved(_, path) => &path.segments,
@@ -486,6 +477,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type {
     }
 }
 
+// FIXME(fmease): Update the `get_*` terminology to the `synthesize_` one.
 pub(crate) fn get_auto_trait_and_blanket_impls(
     cx: &mut DocContext<'_>,
     item_def_id: DefId,
@@ -493,8 +485,8 @@ pub(crate) fn get_auto_trait_and_blanket_impls(
     let auto_impls = cx
         .sess()
         .prof
-        .generic_activity("get_auto_trait_impls")
-        .run(|| AutoTraitFinder::new(cx).get_auto_trait_impls(item_def_id));
+        .generic_activity("synthesize_auto_trait_impls")
+        .run(|| synthesize_auto_trait_impls(cx, item_def_id));
     let blanket_impls = cx
         .sess()
         .prof
diff --git a/tests/rustdoc/synthetic_auto/bounds.rs b/tests/rustdoc/synthetic_auto/bounds.rs
new file mode 100644
index 000000000000..17528d01c8d2
--- /dev/null
+++ b/tests/rustdoc/synthetic_auto/bounds.rs
@@ -0,0 +1,21 @@
+pub struct Outer(Inner);
+pub struct Inner(T);
+
+// @has bounds/struct.Outer.html
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
+// "impl Unpin for Outerwhere \
+//     T: for<'any> Trait = (), X = ()>,"
+
+impl std::marker::Unpin for Inner
+where
+    T: for<'any> Trait = (), X = ()>,
+{}
+
+pub trait Trait: SuperTrait {
+    type A;
+    type B<'a>;
+}
+
+pub trait SuperTrait {
+    type X;
+}
diff --git a/tests/rustdoc/synthetic_auto/complex.rs b/tests/rustdoc/synthetic_auto/complex.rs
index 21055287c996..2722f6d338fe 100644
--- a/tests/rustdoc/synthetic_auto/complex.rs
+++ b/tests/rustdoc/synthetic_auto/complex.rs
@@ -21,8 +21,8 @@ mod foo {
 
 // @has complex/struct.NotOuter.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
-// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K>where 'a: 'static, T: MyTrait<'a>, \
-// K: for<'b> Fn((&'b bool, &'a u8)) -> &'b i8, >::MyItem: Copy,"
+// "impl<'a, T, K> Send for Outer<'a, T, K>where 'a: 'static, T: MyTrait<'a>, \
+// K: for<'b> Fn((&'b bool, &'a u8)) -> &'b i8 + ?Sized, >::MyItem: Copy,"
 
 pub use foo::{Foo, Inner as NotInner, MyTrait as NotMyTrait, Outer as NotOuter};
 

From 748dba2bafab14117f626348b716a824d301cffd Mon Sep 17 00:00:00 2001
From: Ben Kimock 
Date: Mon, 1 Apr 2024 20:17:12 -0400
Subject: [PATCH 169/192] Only allow upstream calls to LLVM intrinsics, not any
 link_name function

---
 compiler/rustc_monomorphize/src/lib.rs | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs
index 9c4a6e69a3cb..2a91d69529a8 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -14,6 +14,7 @@ use rustc_middle::ty::adjustment::CustomCoerceUnsized;
 use rustc_middle::ty::Instance;
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::{self, Ty};
+use rustc_span::def_id::DefId;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::ErrorGuaranteed;
 
@@ -57,13 +58,24 @@ fn custom_coerce_unsize_info<'tcx>(
 /// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is
 /// not guaranteed. So we used this function in codegen backends to ensure we do not generate any
 /// unlinkable calls.
+///
+/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker.
 pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: Instance<'tcx>,
 ) -> bool {
-    !instance.def_id().is_local()
+    fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+        if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name {
+            name.as_str().starts_with("llvm.")
+        } else {
+            false
+        }
+    }
+
+    let def_id = instance.def_id();
+    !def_id.is_local()
         && tcx.is_compiler_builtins(LOCAL_CRATE)
-        && tcx.codegen_fn_attrs(instance.def_id()).link_name.is_none()
+        && !is_llvm_intrinsic(tcx, def_id)
         && !should_codegen_locally(tcx, instance)
 }
 

From f2fd2d8c7080f7a7d770b3e3d27e525250c182dc Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Sun, 31 Mar 2024 17:20:00 -0400
Subject: [PATCH 170/192] Make sure to insert Sized bound first into clauses
 list

---
 compiler/rustc_hir_analysis/src/bounds.rs     | 10 +++++--
 .../incremental/hashes/function_interfaces.rs |  2 +-
 ...verflow-due-to-sized-predicate-ordering.rs | 30 +++++++++++++++++++
 3 files changed, 39 insertions(+), 3 deletions(-)
 create mode 100644 tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs

diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index d5465bb5dd54..d3f51195dfb2 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -54,14 +54,20 @@ impl<'tcx> Bounds<'tcx> {
         span: Span,
         polarity: ty::PredicatePolarity,
     ) {
-        self.clauses.push((
+        let clause = (
             trait_ref
                 .map_bound(|trait_ref| {
                     ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity })
                 })
                 .to_predicate(tcx),
             span,
-        ));
+        );
+        // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands.
+        if tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) {
+            self.clauses.insert(0, clause);
+        } else {
+            self.clauses.push(clause);
+        }
     }
 
     pub fn push_projection_bound(
diff --git a/tests/incremental/hashes/function_interfaces.rs b/tests/incremental/hashes/function_interfaces.rs
index 3be30fab99cf..4fa2762099cd 100644
--- a/tests/incremental/hashes/function_interfaces.rs
+++ b/tests/incremental/hashes/function_interfaces.rs
@@ -217,7 +217,7 @@ pub fn second_trait_bound() {}
 pub fn second_builtin_bound() {}
 
 #[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(cfg = "cfail2", except = "opt_hir_owner_nodes, predicates_of")]
+#[rustc_clean(cfg = "cfail2", except = "opt_hir_owner_nodes")]
 #[rustc_clean(cfg = "cfail3")]
 #[rustc_clean(cfg = "cfail5", except = "opt_hir_owner_nodes, predicates_of")]
 #[rustc_clean(cfg = "cfail6")]
diff --git a/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs b/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs
new file mode 100644
index 000000000000..08e15117c4b2
--- /dev/null
+++ b/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs
@@ -0,0 +1,30 @@
+//@ check-pass
+// Regression test due to #123279
+
+pub trait Job: AsJob {
+    fn run_once(&self);
+}
+
+impl Job for F {
+    fn run_once(&self) {
+        todo!()
+    }
+}
+
+pub trait AsJob {}
+
+// Ensure that `T: Sized + Job` by reordering the explicit `Sized` to where
+// the implicit sized pred would go.
+impl AsJob for T {}
+
+pub struct LoopingJobService {
+    job: Box,
+}
+
+impl Job for LoopingJobService {
+    fn run_once(&self) {
+        self.job.run_once()
+    }
+}
+
+fn main() {}

From 5f59b7f7634ac4b05923b47de00f2a13b213af1f Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Sat, 9 Mar 2024 22:12:33 +0000
Subject: [PATCH 171/192] Instantiate closure-like bounds with placeholders to
 deal with binders correctly

---
 .../src/traits/select/confirmation.rs         | 150 ++++++++++--------
 .../src/traits/select/mod.rs                  |  20 +--
 .../builtin-closure-like-bounds.rs            |  51 ++++++
 3 files changed, 139 insertions(+), 82 deletions(-)
 create mode 100644 tests/ui/higher-ranked/builtin-closure-like-bounds.rs

diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 6f512a1173f5..47f136cb2b77 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -678,17 +678,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         fn_host_effect: ty::Const<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
         debug!(?obligation, "confirm_fn_pointer_candidate");
+        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
+        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
 
         let tcx = self.tcx();
-
-        let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else {
-            // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`,
-            // but we do not currently. Luckily, such a bound is not
-            // particularly useful, so we don't expect users to write
-            // them often.
-            return Err(SelectionError::Unimplemented);
-        };
-
         let sig = self_ty.fn_sig(tcx);
         let trait_ref = closure_trait_ref_and_return_type(
             tcx,
@@ -700,7 +693,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         )
         .map_bound(|(trait_ref, _)| trait_ref);
 
-        let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
+        let mut nested = self.equate_trait_refs(
+            &obligation.cause,
+            obligation.param_env,
+            placeholder_predicate.trait_ref,
+            trait_ref,
+        )?;
         let cause = obligation.derived_cause(BuiltinDerivedObligation);
 
         // Confirm the `type Output: Sized;` bound that is present on `FnOnce`
@@ -748,10 +746,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
-        // Okay to skip binder because the args on coroutine types never
-        // touch bound regions, they just capture the in-scope
-        // type/region parameters.
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
+        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
+        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
         let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
             bug!("closure candidate for non-closure {:?}", obligation);
         };
@@ -760,15 +756,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let coroutine_sig = args.as_coroutine().sig();
 
-        // NOTE: The self-type is a coroutine type and hence is
-        // in fact unparameterized (or at least does not reference any
-        // regions bound in the obligation).
-        let self_ty = obligation
-            .predicate
-            .self_ty()
-            .no_bound_vars()
-            .expect("unboxed closure type should not capture bound vars from the predicate");
-
         let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs(
             self.tcx(),
             obligation.predicate.def_id(),
@@ -776,7 +763,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             coroutine_sig,
         );
 
-        let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
+        let nested = self.equate_trait_refs(
+            &obligation.cause,
+            obligation.param_env,
+            placeholder_predicate.trait_ref,
+            ty::Binder::dummy(trait_ref),
+        )?;
         debug!(?trait_ref, ?nested, "coroutine candidate obligations");
 
         Ok(nested)
@@ -786,10 +778,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
-        // Okay to skip binder because the args on coroutine types never
-        // touch bound regions, they just capture the in-scope
-        // type/region parameters.
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
+        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
+        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
         let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
             bug!("closure candidate for non-closure {:?}", obligation);
         };
@@ -801,11 +791,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let (trait_ref, _) = super::util::future_trait_ref_and_outputs(
             self.tcx(),
             obligation.predicate.def_id(),
-            obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(),
+            self_ty,
             coroutine_sig,
         );
 
-        let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
+        let nested = self.equate_trait_refs(
+            &obligation.cause,
+            obligation.param_env,
+            placeholder_predicate.trait_ref,
+            ty::Binder::dummy(trait_ref),
+        )?;
         debug!(?trait_ref, ?nested, "future candidate obligations");
 
         Ok(nested)
@@ -815,10 +810,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
-        // Okay to skip binder because the args on coroutine types never
-        // touch bound regions, they just capture the in-scope
-        // type/region parameters.
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
+        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
+        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
         let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
             bug!("closure candidate for non-closure {:?}", obligation);
         };
@@ -830,11 +823,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs(
             self.tcx(),
             obligation.predicate.def_id(),
-            obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
+            self_ty,
             gen_sig,
         );
 
-        let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
+        let nested = self.equate_trait_refs(
+            &obligation.cause,
+            obligation.param_env,
+            placeholder_predicate.trait_ref,
+            ty::Binder::dummy(trait_ref),
+        )?;
         debug!(?trait_ref, ?nested, "iterator candidate obligations");
 
         Ok(nested)
@@ -844,10 +842,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
-        // Okay to skip binder because the args on coroutine types never
-        // touch bound regions, they just capture the in-scope
-        // type/region parameters.
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
+        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
+        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
         let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
             bug!("closure candidate for non-closure {:?}", obligation);
         };
@@ -859,11 +855,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs(
             self.tcx(),
             obligation.predicate.def_id(),
-            obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
+            self_ty,
             gen_sig,
         );
 
-        let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
+        let nested = self.equate_trait_refs(
+            &obligation.cause,
+            obligation.param_env,
+            placeholder_predicate.trait_ref,
+            ty::Binder::dummy(trait_ref),
+        )?;
         debug!(?trait_ref, ?nested, "iterator candidate obligations");
 
         Ok(nested)
@@ -874,14 +875,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
-        // Okay to skip binder because the args on closure types never
-        // touch bound regions, they just capture the in-scope
-        // type/region parameters.
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
+        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
+        let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
+
         let trait_ref = match *self_ty.kind() {
-            ty::Closure(_, args) => {
-                self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_)
-            }
+            ty::Closure(..) => self.closure_trait_ref_unnormalized(
+                self_ty,
+                obligation.predicate.def_id(),
+                self.tcx().consts.true_,
+            ),
             ty::CoroutineClosure(_, args) => {
                 args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
                     ty::TraitRef::new(
@@ -896,7 +898,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
         };
 
-        self.confirm_poly_trait_refs(obligation, trait_ref)
+        self.equate_trait_refs(
+            &obligation.cause,
+            obligation.param_env,
+            placeholder_predicate.trait_ref,
+            trait_ref,
+        )
     }
 
     #[instrument(skip(self), level = "debug")]
@@ -904,8 +911,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
+        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
+        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
+
         let tcx = self.tcx();
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
 
         let mut nested = vec![];
         let (trait_ref, kind_ty) = match *self_ty.kind() {
@@ -972,7 +981,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             _ => bug!("expected callable type for AsyncFn candidate"),
         };
 
-        nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?);
+        nested.extend(self.equate_trait_refs(
+            &obligation.cause,
+            obligation.param_env,
+            placeholder_predicate.trait_ref,
+            trait_ref,
+        )?);
 
         let goal_kind =
             self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
@@ -1025,34 +1039,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// selection of the impl. Therefore, if there is a mismatch, we
     /// report an error to the user.
     #[instrument(skip(self), level = "trace")]
-    fn confirm_poly_trait_refs(
+    fn equate_trait_refs(
         &mut self,
-        obligation: &PolyTraitObligation<'tcx>,
-        self_ty_trait_ref: ty::PolyTraitRef<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        obligation_trait_ref: ty::TraitRef<'tcx>,
+        found_trait_ref: ty::PolyTraitRef<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
-        let obligation_trait_ref =
-            self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref());
-        let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
-            obligation.cause.span,
+        let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
+            cause.span,
             HigherRankedType,
-            self_ty_trait_ref,
+            found_trait_ref,
         );
         // Normalize the obligation and expected trait refs together, because why not
-        let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } =
+        let Normalized { obligations: nested, value: (obligation_trait_ref, found_trait_ref) } =
             ensure_sufficient_stack(|| {
                 normalize_with_depth(
                     self,
-                    obligation.param_env,
-                    obligation.cause.clone(),
-                    obligation.recursion_depth + 1,
-                    (obligation_trait_ref, self_ty_trait_ref),
+                    param_env,
+                    cause.clone(),
+                    0,
+                    (obligation_trait_ref, found_trait_ref),
                 )
             });
 
         // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs
         self.infcx
-            .at(&obligation.cause, obligation.param_env)
-            .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref)
+            .at(&cause, param_env)
+            .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, found_trait_ref)
             .map(|InferOk { mut obligations, .. }| {
                 obligations.extend(nested);
                 obligations
@@ -1060,7 +1074,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             .map_err(|terr| {
                 SignatureMismatch(Box::new(SignatureMismatchData {
                     expected_trait_ref: ty::Binder::dummy(obligation_trait_ref),
-                    found_trait_ref: ty::Binder::dummy(expected_trait_ref),
+                    found_trait_ref: ty::Binder::dummy(found_trait_ref),
                     terr,
                 }))
             })
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 1894fbba3027..084f7b1a8c21 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -2679,26 +2679,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
     #[instrument(skip(self), level = "debug")]
     fn closure_trait_ref_unnormalized(
         &mut self,
-        obligation: &PolyTraitObligation<'tcx>,
-        args: GenericArgsRef<'tcx>,
+        self_ty: Ty<'tcx>,
+        fn_trait_def_id: DefId,
         fn_host_effect: ty::Const<'tcx>,
     ) -> ty::PolyTraitRef<'tcx> {
+        let ty::Closure(_, args) = *self_ty.kind() else {
+            bug!("expected closure, found {self_ty}");
+        };
         let closure_sig = args.as_closure().sig();
 
-        debug!(?closure_sig);
-
-        // NOTE: The self-type is an unboxed closure type and hence is
-        // in fact unparameterized (or at least does not reference any
-        // regions bound in the obligation).
-        let self_ty = obligation
-            .predicate
-            .self_ty()
-            .no_bound_vars()
-            .expect("unboxed closure type should not capture bound vars from the predicate");
-
         closure_trait_ref_and_return_type(
             self.tcx(),
-            obligation.predicate.def_id(),
+            fn_trait_def_id,
             self_ty,
             closure_sig,
             util::TupleArgumentsFlag::No,
diff --git a/tests/ui/higher-ranked/builtin-closure-like-bounds.rs b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs
new file mode 100644
index 000000000000..c7dfaefbc74b
--- /dev/null
+++ b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs
@@ -0,0 +1,51 @@
+//@ edition:2024
+//@ compile-flags: -Zunstable-options
+//@ revisions: current next
+//@[next] compile-flags: -Znext-solver
+
+#![feature(unboxed_closures, gen_blocks)]
+
+trait Dispatch {
+    fn dispatch(self);
+}
+
+struct Fut(T);
+impl Fn<(&'a (),)>> Dispatch for Fut
+where
+    for<'a> >::Output: Future,
+{
+    fn dispatch(self) {
+        (self.0)(&());
+    }
+}
+
+struct Gen(T);
+impl Fn<(&'a (),)>> Dispatch for Gen
+where
+    for<'a> >::Output: Iterator,
+{
+    fn dispatch(self) {
+        (self.0)(&());
+    }
+}
+
+struct Closure(T);
+impl Fn<(&'a (),)>> Dispatch for Closure
+where
+    for<'a> >::Output: Fn<(&'a (),)>,
+{
+    fn dispatch(self) {
+        (self.0)(&())(&());
+    }
+}
+
+fn main() {
+    async fn foo(_: &()) {}
+    Fut(foo).dispatch();
+
+    gen fn bar(_: &()) {}
+    Gen(bar).dispatch();
+
+    fn uwu<'a>(x: &'a ()) -> impl Fn(&'a ()) { |_| {} }
+    Closure(uwu).dispatch();
+}
\ No newline at end of file

From 09ea3f93ee6497247172a80ba17493748d08b64c Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Sun, 10 Mar 2024 05:14:11 +0000
Subject: [PATCH 172/192] Fix obligation param and bless tests

---
 .../src/traits/select/confirmation.rs         | 58 ++++++-------------
 .../builtin-closure-like-bounds.rs            |  9 ++-
 .../closure-bound-codegen-ice.rs              | 33 +++++++++++
 .../trait-bounds/fn-ptr.classic.stderr        | 19 ------
 .../trait-bounds/fn-ptr.current.stderr        | 19 ------
 tests/ui/higher-ranked/trait-bounds/fn-ptr.rs |  3 +-
 .../trait-bounds/future.classic.stderr        |  6 --
 .../trait-bounds/future.current.stderr        |  6 --
 tests/ui/higher-ranked/trait-bounds/future.rs |  9 +--
 tests/ui/lifetimes/issue-105675.rs            |  2 +-
 .../lifetimes/lifetime-errors/issue_74400.rs  |  4 +-
 11 files changed, 64 insertions(+), 104 deletions(-)
 create mode 100644 tests/ui/higher-ranked/closure-bound-codegen-ice.rs
 delete mode 100644 tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr
 delete mode 100644 tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr
 delete mode 100644 tests/ui/higher-ranked/trait-bounds/future.classic.stderr
 delete mode 100644 tests/ui/higher-ranked/trait-bounds/future.current.stderr

diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 47f136cb2b77..0459246553b0 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -28,7 +28,7 @@ use crate::traits::{
     BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
     ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, PolyTraitObligation,
     PredicateObligation, Selection, SelectionError, SignatureMismatch, TraitNotObjectSafe,
-    Unimplemented,
+    TraitObligation, Unimplemented,
 };
 
 use super::BuiltinImplConditions;
@@ -693,12 +693,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         )
         .map_bound(|(trait_ref, _)| trait_ref);
 
-        let mut nested = self.equate_trait_refs(
-            &obligation.cause,
-            obligation.param_env,
-            placeholder_predicate.trait_ref,
-            trait_ref,
-        )?;
+        let mut nested =
+            self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?;
         let cause = obligation.derived_cause(BuiltinDerivedObligation);
 
         // Confirm the `type Output: Sized;` bound that is present on `FnOnce`
@@ -764,9 +760,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
 
         let nested = self.equate_trait_refs(
-            &obligation.cause,
-            obligation.param_env,
-            placeholder_predicate.trait_ref,
+            obligation.with(self.tcx(), placeholder_predicate),
             ty::Binder::dummy(trait_ref),
         )?;
         debug!(?trait_ref, ?nested, "coroutine candidate obligations");
@@ -796,9 +790,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
 
         let nested = self.equate_trait_refs(
-            &obligation.cause,
-            obligation.param_env,
-            placeholder_predicate.trait_ref,
+            obligation.with(self.tcx(), placeholder_predicate),
             ty::Binder::dummy(trait_ref),
         )?;
         debug!(?trait_ref, ?nested, "future candidate obligations");
@@ -828,9 +820,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
 
         let nested = self.equate_trait_refs(
-            &obligation.cause,
-            obligation.param_env,
-            placeholder_predicate.trait_ref,
+            obligation.with(self.tcx(), placeholder_predicate),
             ty::Binder::dummy(trait_ref),
         )?;
         debug!(?trait_ref, ?nested, "iterator candidate obligations");
@@ -860,9 +850,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
 
         let nested = self.equate_trait_refs(
-            &obligation.cause,
-            obligation.param_env,
-            placeholder_predicate.trait_ref,
+            obligation.with(self.tcx(), placeholder_predicate),
             ty::Binder::dummy(trait_ref),
         )?;
         debug!(?trait_ref, ?nested, "iterator candidate obligations");
@@ -898,12 +886,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
         };
 
-        self.equate_trait_refs(
-            &obligation.cause,
-            obligation.param_env,
-            placeholder_predicate.trait_ref,
-            trait_ref,
-        )
+        self.equate_trait_refs(obligation.with(self.tcx(), placeholder_predicate), trait_ref)
     }
 
     #[instrument(skip(self), level = "debug")]
@@ -981,12 +964,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             _ => bug!("expected callable type for AsyncFn candidate"),
         };
 
-        nested.extend(self.equate_trait_refs(
-            &obligation.cause,
-            obligation.param_env,
-            placeholder_predicate.trait_ref,
-            trait_ref,
-        )?);
+        nested.extend(
+            self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?,
+        );
 
         let goal_kind =
             self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
@@ -1041,13 +1021,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     #[instrument(skip(self), level = "trace")]
     fn equate_trait_refs(
         &mut self,
-        cause: &ObligationCause<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        obligation_trait_ref: ty::TraitRef<'tcx>,
+        obligation: TraitObligation<'tcx>,
         found_trait_ref: ty::PolyTraitRef<'tcx>,
     ) -> Result>, SelectionError<'tcx>> {
         let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
-            cause.span,
+            obligation.cause.span,
             HigherRankedType,
             found_trait_ref,
         );
@@ -1056,16 +1034,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             ensure_sufficient_stack(|| {
                 normalize_with_depth(
                     self,
-                    param_env,
-                    cause.clone(),
-                    0,
-                    (obligation_trait_ref, found_trait_ref),
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    (obligation.predicate.trait_ref, found_trait_ref),
                 )
             });
 
         // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs
         self.infcx
-            .at(&cause, param_env)
+            .at(&obligation.cause, obligation.param_env)
             .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, found_trait_ref)
             .map(|InferOk { mut obligations, .. }| {
                 obligations.extend(nested);
diff --git a/tests/ui/higher-ranked/builtin-closure-like-bounds.rs b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs
index c7dfaefbc74b..dee290cc4396 100644
--- a/tests/ui/higher-ranked/builtin-closure-like-bounds.rs
+++ b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs
@@ -2,6 +2,13 @@
 //@ compile-flags: -Zunstable-options
 //@ revisions: current next
 //@[next] compile-flags: -Znext-solver
+//@ check-pass
+
+// Makes sure that we support closure/coroutine goals where the signature of
+// the item references higher-ranked lifetimes from the *predicate* binder,
+// not its own internal signature binder.
+//
+// This was fixed in .
 
 #![feature(unboxed_closures, gen_blocks)]
 
@@ -48,4 +55,4 @@ fn main() {
 
     fn uwu<'a>(x: &'a ()) -> impl Fn(&'a ()) { |_| {} }
     Closure(uwu).dispatch();
-}
\ No newline at end of file
+}
diff --git a/tests/ui/higher-ranked/closure-bound-codegen-ice.rs b/tests/ui/higher-ranked/closure-bound-codegen-ice.rs
new file mode 100644
index 000000000000..4d7ae12d7a74
--- /dev/null
+++ b/tests/ui/higher-ranked/closure-bound-codegen-ice.rs
@@ -0,0 +1,33 @@
+//@ revisions: current next
+//@[next] compile-flags: -Znext-solver
+//@ build-pass
+
+// Regression test for incomplete handling of Fn-trait goals,
+// fixed in #122267.
+
+trait Trait {
+    type Assoc<'a>: FnOnce(&'a ());
+}
+
+impl Trait for () {
+    type Assoc<'a> = fn(&'a ());
+}
+
+trait Indir {
+    fn break_me() {}
+}
+
+impl Indir for F
+where
+    for<'a> F::Assoc<'a>: FnOnce(&'a ()),
+{
+    fn break_me() {}
+}
+
+fn foo() {
+    F::break_me()
+}
+
+fn main() {
+    foo::<()>();
+}
diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr b/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr
deleted file mode 100644
index b322ea41c436..000000000000
--- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error[E0277]: expected a `Fn(&'w ())` closure, found `fn(&'w ())`
-  --> $DIR/fn-ptr.rs:12:5
-   |
-LL |     ice();
-   |     ^^^^^ expected an `Fn(&'w ())` closure, found `fn(&'w ())`
-   |
-   = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())`
-note: required by a bound in `ice`
-  --> $DIR/fn-ptr.rs:7:25
-   |
-LL | fn ice()
-   |    --- required by a bound in this function
-LL | where
-LL |     for<'w> fn(&'w ()): Fn(&'w ()),
-   |                         ^^^^^^^^^^ required by this bound in `ice`
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr b/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr
deleted file mode 100644
index f3583cd218b8..000000000000
--- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error[E0277]: expected a `Fn(&'w ())` closure, found `fn(&'w ())`
-  --> $DIR/fn-ptr.rs:13:5
-   |
-LL |     ice();
-   |     ^^^^^ expected an `Fn(&'w ())` closure, found `fn(&'w ())`
-   |
-   = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())`
-note: required by a bound in `ice`
-  --> $DIR/fn-ptr.rs:8:25
-   |
-LL | fn ice()
-   |    --- required by a bound in this function
-LL | where
-LL |     for<'w> fn(&'w ()): Fn(&'w ()),
-   |                         ^^^^^^^^^^ required by this bound in `ice`
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs b/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs
index 9298c10c3417..7a4c15f4d4b1 100644
--- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs
+++ b/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs
@@ -1,7 +1,7 @@
 //@ revisions: current next
 //@ ignore-compare-mode-next-solver (explicit revisions)
 //@[next] compile-flags: -Znext-solver
-//@[next] check-pass
+//@ check-pass
 
 fn ice()
 where
@@ -11,5 +11,4 @@ where
 
 fn main() {
     ice();
-    //[current]~^ ERROR expected a `Fn(&'w ())` closure, found `fn(&'w ())`
 }
diff --git a/tests/ui/higher-ranked/trait-bounds/future.classic.stderr b/tests/ui/higher-ranked/trait-bounds/future.classic.stderr
deleted file mode 100644
index ef31b7266c7e..000000000000
--- a/tests/ui/higher-ranked/trait-bounds/future.classic.stderr
+++ /dev/null
@@ -1,6 +0,0 @@
-error: the compiler unexpectedly panicked. this is a bug.
-
-query stack during panic:
-#0 [evaluate_obligation] evaluating trait selection obligation `for<'a> {async fn body@$DIR/future.rs:32:35: 34:2}: core::future::future::Future`
-#1 [codegen_select_candidate] computing candidate for ``
-end of query stack
diff --git a/tests/ui/higher-ranked/trait-bounds/future.current.stderr b/tests/ui/higher-ranked/trait-bounds/future.current.stderr
deleted file mode 100644
index 673bc48a424e..000000000000
--- a/tests/ui/higher-ranked/trait-bounds/future.current.stderr
+++ /dev/null
@@ -1,6 +0,0 @@
-error: the compiler unexpectedly panicked. this is a bug.
-
-query stack during panic:
-#0 [evaluate_obligation] evaluating trait selection obligation `for<'a> {async fn body of strlen()}: core::future::future::Future`
-#1 [codegen_select_candidate] computing candidate for ``
-end of query stack
diff --git a/tests/ui/higher-ranked/trait-bounds/future.rs b/tests/ui/higher-ranked/trait-bounds/future.rs
index 4b52f04dbe05..7105015b6901 100644
--- a/tests/ui/higher-ranked/trait-bounds/future.rs
+++ b/tests/ui/higher-ranked/trait-bounds/future.rs
@@ -3,14 +3,7 @@
 //@ revisions: current next
 //@ ignore-compare-mode-next-solver (explicit revisions)
 //@[next] compile-flags: -Znext-solver
-//@[next] check-pass
-//@[current] known-bug: #112347
-//@[current] build-fail
-//@[current] failure-status: 101
-//@[current] normalize-stderr-test "note: .*\n\n" -> ""
-//@[current] normalize-stderr-test "thread 'rustc' panicked.*\n.*\n" -> ""
-//@[current] normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: "
-//@[current] rustc-env:RUST_BACKTRACE=0
+//@ check-pass
 
 #![feature(unboxed_closures)]
 
diff --git a/tests/ui/lifetimes/issue-105675.rs b/tests/ui/lifetimes/issue-105675.rs
index 2e2eaca0d335..0472537e7f34 100644
--- a/tests/ui/lifetimes/issue-105675.rs
+++ b/tests/ui/lifetimes/issue-105675.rs
@@ -4,7 +4,7 @@ fn main() {
     let f = | _ , y: &u32 , z | ();
     thing(f);
     //~^ ERROR implementation of `FnOnce` is not general enough
-    //~^^ ERROR implementation of `FnOnce` is not general enough
+    //~| ERROR implementation of `FnOnce` is not general enough
     let f = | x, y: _  , z: u32 | ();
     thing(f);
     //~^ ERROR implementation of `FnOnce` is not general enough
diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs
index b02e38bec3b3..72345fa294a4 100644
--- a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs
+++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs
@@ -1,5 +1,5 @@
 //! Regression test for #74400: Type mismatch in function arguments E0631, E0271 are falsely
-//! recognized as E0308 mismatched types.
+//! recognized as "implementation of `FnOnce` is not general enough".
 
 use std::convert::identity;
 
@@ -13,6 +13,6 @@ fn g(data: &[T]) {
     //~^ ERROR the parameter type
     //~| ERROR the parameter type
     //~| ERROR the parameter type
-    //~| ERROR implementation of `FnOnce` is not general
+    //~| ERROR implementation of `FnOnce` is not general enough
     //~| ERROR implementation of `Fn` is not general enough
 }

From 6ad96825fcd8002c0e4234b020d2eb3ad79232e0 Mon Sep 17 00:00:00 2001
From: Steve Lau 
Date: Tue, 2 Apr 2024 14:18:31 +0800
Subject: [PATCH 173/192] fix: build on haiku by adding missing import

---
 library/std/src/sys/pal/unix/thread.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index a9ed7e7c75e8..3e3a54c3c745 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -278,7 +278,7 @@ impl Thread {
                 return None;
             }
             let info = tinfo.assume_init();
-            let name = slice::from_raw_parts(info.name.as_ptr() as *const u8, info.name.len());
+            let name = core::slice::from_raw_parts(info.name.as_ptr() as *const u8, info.name.len());
             CStr::from_bytes_until_nul(name).map(CStr::to_owned).ok()
         }
     }

From bb439900ddaeb9ad7119508f9e1b9c7aa07b9f91 Mon Sep 17 00:00:00 2001
From: Steve Lau 
Date: Tue, 2 Apr 2024 14:29:38 +0800
Subject: [PATCH 174/192] style: fmt

---
 library/std/src/sys/pal/unix/thread.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 3e3a54c3c745..7d25c974ed36 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -278,7 +278,8 @@ impl Thread {
                 return None;
             }
             let info = tinfo.assume_init();
-            let name = core::slice::from_raw_parts(info.name.as_ptr() as *const u8, info.name.len());
+            let name =
+                core::slice::from_raw_parts(info.name.as_ptr() as *const u8, info.name.len());
             CStr::from_bytes_until_nul(name).map(CStr::to_owned).ok()
         }
     }

From 0bb1ec729f5caf04b93a22d6aabda24bf187a12b Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Tue, 2 Apr 2024 10:42:32 +0200
Subject: [PATCH 175/192] Remove redundant code comments

---
 tests/rustdoc-gui/search-result-color.goml | 144 ++++++++++-----------
 1 file changed, 72 insertions(+), 72 deletions(-)

diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml
index 8b0ca7ec97c4..1a19ea2d8432 100644
--- a/tests/rustdoc-gui/search-result-color.goml
+++ b/tests/rustdoc-gui/search-result-color.goml
@@ -79,58 +79,58 @@ store-value: (grey, "#999")
 
 call-function: (
     "check-result-color", {
-        "result_kind": "keyword", // item kind
-        "color": "#39afd7", // color of item kind
-        "hover_color": "#39afd7", // color of hovered/focused item kind
+        "result_kind": "keyword",
+        "color": "#39afd7",
+        "hover_color": "#39afd7",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "struct", // item kind
-        "color": "#ffa0a5", // color of item kind
-        "hover_color": "#ffa0a5", // color of hovered/focused item kind
+        "result_kind": "struct",
+        "color": "#ffa0a5",
+        "hover_color": "#ffa0a5",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "associatedtype", // item kind
-        "color": "#39afd7", // color of item kind
-        "hover_color": "#39afd7", // color of hovered/focused item kind
+        "result_kind": "associatedtype",
+        "color": "#39afd7",
+        "hover_color": "#39afd7",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "tymethod", // item kind
-        "color": "#fdd687", // color of item kind
-        "hover_color": "#fdd687", // color of hovered/focused item kind
+        "result_kind": "tymethod",
+        "color": "#fdd687",
+        "hover_color": "#fdd687",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "method", // item kind
-        "color": "#fdd687", // color of item kind
-        "hover_color": "#fdd687", // color of hovered/focused item kind
+        "result_kind": "method",
+        "color": "#fdd687",
+        "hover_color": "#fdd687",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "structfield", // item kind
-        "color": "#0096cf", // color of item kind
-        "hover_color": "#fff", // color of hovered/focused item kind
+        "result_kind": "structfield",
+        "color": "#0096cf",
+        "hover_color": "#fff",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "macro", // item kind
-        "color": "#a37acc", // color of item kind
-        "hover_color": "#a37acc", // color of hovered/focused item kind
+        "result_kind": "macro",
+        "color": "#a37acc",
+        "hover_color": "#a37acc",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "fn", // item kind
-        "color": "#fdd687", // color of item kind
-        "hover_color": "#fdd687", // color of hovered/focused item kind
+        "result_kind": "fn",
+        "color": "#fdd687",
+        "hover_color": "#fdd687",
     },
 )
 
@@ -191,58 +191,58 @@ store-value: (grey, "#ccc")
 
 call-function: (
     "check-result-color", {
-        "result_kind": "keyword", // item kind
-        "color": "#d2991d", // color of item kind
-        "hover_color": "#d2991d", // color of hovered/focused item kind
+        "result_kind": "keyword",
+        "color": "#d2991d",
+        "hover_color": "#d2991d",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "struct", // item kind
-        "color": "#2dbfb8", // color of item kind
-        "hover_color": "#2dbfb8", // color of hovered/focused item kind
+        "result_kind": "struct",
+        "color": "#2dbfb8",
+        "hover_color": "#2dbfb8",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "associatedtype", // item kind
-        "color": "#d2991d", // color of item kind
-        "hover_color": "#d2991d", // color of hovered/focused item kind
+        "result_kind": "associatedtype",
+        "color": "#d2991d",
+        "hover_color": "#d2991d",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "tymethod", // item kind
-        "color": "#2bab63", // color of item kind
-        "hover_color": "#2bab63", // color of hovered/focused item kind
+        "result_kind": "tymethod",
+        "color": "#2bab63",
+        "hover_color": "#2bab63",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "method", // item kind
-        "color": "#2bab63", // color of item kind
-        "hover_color": "#2bab63", // color of hovered/focused item kind
+        "result_kind": "method",
+        "color": "#2bab63",
+        "hover_color": "#2bab63",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "structfield", // item kind
-        "color": "#ddd", // color of item kind
-        "hover_color": "#ddd", // color of hovered/focused item kind
+        "result_kind": "structfield",
+        "color": "#ddd",
+        "hover_color": "#ddd",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "macro", // item kind
-        "color": "#09bd00", // color of item kind
-        "hover_color": "#09bd00", // color of hovered/focused item kind
+        "result_kind": "macro",
+        "color": "#09bd00",
+        "hover_color": "#09bd00",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "fn", // item kind
-        "color": "#2bab63", // color of item kind
-        "hover_color": "#2bab63", // color of hovered/focused item kind
+        "result_kind": "fn",
+        "color": "#2bab63",
+        "hover_color": "#2bab63",
     },
 )
 
@@ -288,58 +288,58 @@ store-value: (grey, "#999")
 
 call-function: (
     "check-result-color", {
-        "result_kind": "keyword", // item kind
-        "color": "#3873ad", // color of item kind
-        "hover_color": "#3873ad", // color of hovered/focused item kind
+        "result_kind": "keyword",
+        "color": "#3873ad",
+        "hover_color": "#3873ad",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "struct", // item kind
-        "color": "#ad378a", // color of item kind
-        "hover_color": "#ad378a", // color of hovered/focused item kind
+        "result_kind": "struct",
+        "color": "#ad378a",
+        "hover_color": "#ad378a",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "associatedtype", // item kind
-        "color": "#3873ad", // color of item kind
-        "hover_color": "#3873ad", // color of hovered/focused item kind
+        "result_kind": "associatedtype",
+        "color": "#3873ad",
+        "hover_color": "#3873ad",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "tymethod", // item kind
-        "color": "#ad7c37", // color of item kind
-        "hover_color": "#ad7c37", // color of hovered/focused item kind
+        "result_kind": "tymethod",
+        "color": "#ad7c37",
+        "hover_color": "#ad7c37",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "method", // item kind
-        "color": "#ad7c37", // color of item kind
-        "hover_color": "#ad7c37", // color of hovered/focused item kind
+        "result_kind": "method",
+        "color": "#ad7c37",
+        "hover_color": "#ad7c37",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "structfield", // item kind
-        "color": "#000", // color of item kind
-        "hover_color": "#000", // color of hovered/focused item kind
+        "result_kind": "structfield",
+        "color": "#000",
+        "hover_color": "#000",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "macro", // item kind
-        "color": "#068000", // color of item kind
-        "hover_color": "#068000", // color of hovered/focused item kind
+        "result_kind": "macro",
+        "color": "#068000",
+        "hover_color": "#068000",
     },
 )
 call-function: (
     "check-result-color", {
-        "result_kind": "fn", // item kind
-        "color": "#ad7c37", // color of item kind
-        "hover_color": "#ad7c37", // color of hovered/focused item kind
+        "result_kind": "fn",
+        "color": "#ad7c37",
+        "hover_color": "#ad7c37",
     },
 )
 

From 6c5c48e880ca0668c5c8a8029769636831985137 Mon Sep 17 00:00:00 2001
From: Oli Scherer 
Date: Tue, 2 Apr 2024 12:06:52 +0000
Subject: [PATCH 176/192] Check that nested statics in thread locals are
 duplicated per thread.

---
 tests/ui/statics/nested_thread_local.rs | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 tests/ui/statics/nested_thread_local.rs

diff --git a/tests/ui/statics/nested_thread_local.rs b/tests/ui/statics/nested_thread_local.rs
new file mode 100644
index 000000000000..10e0a3420d96
--- /dev/null
+++ b/tests/ui/statics/nested_thread_local.rs
@@ -0,0 +1,24 @@
+// Check that nested statics in thread locals are
+// duplicated per thread.
+
+#![feature(const_refs_to_cell)]
+#![feature(thread_local)]
+
+//@run-pass
+
+#[thread_local]
+static mut FOO: &mut u32 = &mut 42;
+
+fn main() {
+    unsafe {
+        *FOO = 1;
+
+        let _ = std::thread::spawn(|| {
+            assert_eq!(*FOO, 42);
+            *FOO = 99;
+        })
+        .join();
+
+        assert_eq!(*FOO, 1);
+    }
+}

From 1012218ba8e641f612e486b925214a4522357bec Mon Sep 17 00:00:00 2001
From: surechen 
Date: Fri, 22 Mar 2024 14:20:31 +0800
Subject: [PATCH 177/192] t	plit astconv's error report code in check
 functions to mod errors.

Move some error report codes to mod `astconv/errors.rs`
---
 .../src/hir_ty_lowering/errors.rs             | 388 +++++++++++++++++-
 .../src/hir_ty_lowering/mod.rs                | 357 +++-------------
 .../src/hir_ty_lowering/object_safety.rs      |  45 +-
 .../rustc_hir_typeck/src/fn_ctxt/_impl.rs     |   7 +-
 .../rustc_trait_selection/src/traits/mod.rs   |   2 +-
 ...dings-in-pattern-with-ty-err-doesnt-ice.rs |   2 +-
 ...s-in-pattern-with-ty-err-doesnt-ice.stderr |   4 +-
 7 files changed, 456 insertions(+), 349 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index ca2e14ee3591..7a0890e50dac 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -1,6 +1,6 @@
 use crate::errors::{
     self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
-    ParenthesizedFnTraitExpansion,
+    ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
 };
 use crate::fluent_generated as fluent;
 use crate::hir_ty_lowering::HirTyLowerer;
@@ -8,19 +8,26 @@ use crate::traits::error_reporting::report_object_safety_error;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordMap;
+use rustc_errors::MultiSpan;
 use rustc_errors::{
     codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
 };
 use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_infer::traits::FulfillmentError;
 use rustc_middle::query::Key;
-use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, suggest_constraining_type_param};
+use rustc_middle::ty::{AdtDef, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{Binder, TraitRef};
 use rustc_session::parse::feature_err;
 use rustc_span::edit_distance::find_best_match_for_name;
-use rustc_span::symbol::{sym, Ident};
+use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::BytePos;
 use rustc_span::{Span, Symbol, DUMMY_SP};
-use rustc_trait_selection::traits::object_safety_violations_for_assoc_item;
+use rustc_trait_selection::traits::{
+    object_safety_violations_for_assoc_item, TraitAliasExpansionInfo,
+};
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     /// On missing type parameters, emit an E0393 error and provide a structured suggestion using
@@ -1024,6 +1031,170 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             Ok(())
         }
     }
+
+    pub fn report_prohibit_generics_error<'a>(
+        &self,
+        segments: impl Iterator> + Clone,
+        args_visitors: impl Iterator> + Clone,
+        err_extend: GenericsArgsErrExtend<'_>,
+    ) -> ErrorGuaranteed {
+        #[derive(PartialEq, Eq, Hash)]
+        enum ProhibitGenericsArg {
+            Lifetime,
+            Type,
+            Const,
+            Infer,
+        }
+
+        let mut prohibit_args = FxIndexSet::default();
+        args_visitors.for_each(|arg| {
+            match arg {
+                hir::GenericArg::Lifetime(_) => prohibit_args.insert(ProhibitGenericsArg::Lifetime),
+                hir::GenericArg::Type(_) => prohibit_args.insert(ProhibitGenericsArg::Type),
+                hir::GenericArg::Const(_) => prohibit_args.insert(ProhibitGenericsArg::Const),
+                hir::GenericArg::Infer(_) => prohibit_args.insert(ProhibitGenericsArg::Infer),
+            };
+        });
+
+        let types_and_spans: Vec<_> = segments
+            .clone()
+            .flat_map(|segment| {
+                if segment.args().args.is_empty() {
+                    None
+                } else {
+                    Some((
+                        match segment.res {
+                            hir::def::Res::PrimTy(ty) => {
+                                format!("{} `{}`", segment.res.descr(), ty.name())
+                            }
+                            hir::def::Res::Def(_, def_id)
+                                if let Some(name) = self.tcx().opt_item_name(def_id) =>
+                            {
+                                format!("{} `{name}`", segment.res.descr())
+                            }
+                            hir::def::Res::Err => "this type".to_string(),
+                            _ => segment.res.descr().to_string(),
+                        },
+                        segment.ident.span,
+                    ))
+                }
+            })
+            .collect();
+        let this_type = match &types_and_spans[..] {
+            [.., _, (last, _)] => format!(
+                "{} and {last}",
+                types_and_spans[..types_and_spans.len() - 1]
+                    .iter()
+                    .map(|(x, _)| x.as_str())
+                    .intersperse(", ")
+                    .collect::()
+            ),
+            [(only, _)] => only.to_string(),
+            [] => "this type".to_string(),
+        };
+
+        let arg_spans: Vec = segments
+            .clone()
+            .flat_map(|segment| segment.args().args)
+            .map(|arg| arg.span())
+            .collect();
+
+        let mut kinds = Vec::with_capacity(4);
+        prohibit_args.iter().for_each(|arg| match arg {
+            ProhibitGenericsArg::Lifetime => kinds.push("lifetime"),
+            ProhibitGenericsArg::Type => kinds.push("type"),
+            ProhibitGenericsArg::Const => kinds.push("const"),
+            ProhibitGenericsArg::Infer => kinds.push("generic"),
+        });
+
+        let (kind, s) = match kinds[..] {
+            [.., _, last] => (
+                format!(
+                    "{} and {last}",
+                    kinds[..kinds.len() - 1]
+                        .iter()
+                        .map(|&x| x)
+                        .intersperse(", ")
+                        .collect::()
+                ),
+                "s",
+            ),
+            [only] => (only.to_string(), ""),
+            [] => unreachable!("expected at least one generic to prohibit"),
+        };
+        let last_span = *arg_spans.last().unwrap();
+        let span: MultiSpan = arg_spans.into();
+        let mut err = struct_span_code_err!(
+            self.tcx().dcx(),
+            span,
+            E0109,
+            "{kind} arguments are not allowed on {this_type}",
+        );
+        err.span_label(last_span, format!("{kind} argument{s} not allowed"));
+        for (what, span) in types_and_spans {
+            err.span_label(span, format!("not allowed on {what}"));
+        }
+        generics_args_err_extend(self.tcx(), segments.clone(), &mut err, err_extend);
+        let reported = err.emit();
+        self.set_tainted_by_errors(reported);
+        reported
+    }
+
+    pub fn report_trait_object_addition_traits_error(
+        &self,
+        regular_traits: &Vec>,
+    ) -> ErrorGuaranteed {
+        let tcx = self.tcx();
+        let first_trait = ®ular_traits[0];
+        let additional_trait = ®ular_traits[1];
+        let mut err = struct_span_code_err!(
+            tcx.dcx(),
+            additional_trait.bottom().1,
+            E0225,
+            "only auto traits can be used as additional traits in a trait object"
+        );
+        additional_trait.label_with_exp_info(
+            &mut err,
+            "additional non-auto trait",
+            "additional use",
+        );
+        first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
+        err.help(format!(
+            "consider creating a new trait with all of these as supertraits and using that \
+             trait here instead: `trait NewTrait: {} {{}}`",
+            regular_traits
+                .iter()
+                // FIXME: This should `print_sugared`, but also needs to integrate projection bounds...
+                .map(|t| t.trait_ref().print_only_trait_path().to_string())
+                .collect::>()
+                .join(" + "),
+        ));
+        err.note(
+            "auto-traits like `Send` and `Sync` are traits that have special properties; \
+             for more information on them, visit \
+             ",
+        );
+        let reported = err.emit();
+        self.set_tainted_by_errors(reported);
+        reported
+    }
+
+    pub fn report_trait_object_with_no_traits_error(
+        &self,
+        span: Span,
+        trait_bounds: &Vec<(Binder<'tcx, TraitRef<'tcx>>, Span)>,
+    ) -> ErrorGuaranteed {
+        let tcx = self.tcx();
+        let trait_alias_span = trait_bounds
+            .iter()
+            .map(|&(trait_ref, _)| trait_ref.def_id())
+            .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
+            .map(|trait_ref| tcx.def_span(trait_ref));
+        let reported =
+            tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
+        self.set_tainted_by_errors(reported);
+        reported
+    }
 }
 
 /// Emits an error regarding forbidden type binding associations
@@ -1031,7 +1202,7 @@ pub fn prohibit_assoc_item_binding(
     tcx: TyCtxt<'_>,
     span: Span,
     segment: Option<(&hir::PathSegment<'_>, Span)>,
-) {
+) -> ErrorGuaranteed {
     tcx.dcx().emit_err(AssocTypeBindingNotAllowed {
         span,
         fn_trait_expansion: if let Some((segment, span)) = segment
@@ -1044,7 +1215,7 @@ pub fn prohibit_assoc_item_binding(
         } else {
             None
         },
-    });
+    })
 }
 
 pub(crate) fn fn_trait_to_string(
@@ -1099,3 +1270,208 @@ pub(crate) fn fn_trait_to_string(
         format!("{}<{}, Output={}>", trait_segment.ident, args, ret)
     }
 }
+
+/// Used for generics args error extend.
+pub enum GenericsArgsErrExtend<'tcx> {
+    EnumVariant {
+        qself: &'tcx hir::Ty<'tcx>,
+        assoc_segment: &'tcx hir::PathSegment<'tcx>,
+        adt_def: AdtDef<'tcx>,
+    },
+    OpaqueTy,
+    PrimTy(hir::PrimTy),
+    SelfTyAlias {
+        def_id: DefId,
+        span: Span,
+    },
+    SelfTyParam(Span),
+    TyParam(DefId),
+    DefVariant,
+    None,
+}
+
+fn generics_args_err_extend<'a>(
+    tcx: TyCtxt<'_>,
+    segments: impl Iterator> + Clone,
+    err: &mut Diag<'_>,
+    err_extend: GenericsArgsErrExtend<'_>,
+) {
+    match err_extend {
+        GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def } => {
+            err.note("enum variants can't have type parameters");
+            let type_name = tcx.item_name(adt_def.did());
+            let msg = format!(
+                "you might have meant to specify type parameters on enum \
+                `{type_name}`"
+            );
+            let Some(args) = assoc_segment.args else {
+                return;
+            };
+            // Get the span of the generics args *including* the leading `::`.
+            // We do so by stretching args.span_ext to the left by 2. Earlier
+            // it was done based on the end of assoc segment but that sometimes
+            // led to impossible spans and caused issues like #116473
+            let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2));
+            if tcx.generics_of(adt_def.did()).count() == 0 {
+                // FIXME(estebank): we could also verify that the arguments being
+                // work for the `enum`, instead of just looking if it takes *any*.
+                err.span_suggestion_verbose(
+                    args_span,
+                    format!("{type_name} doesn't have generic parameters"),
+                    "",
+                    Applicability::MachineApplicable,
+                );
+                return;
+            }
+            let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else {
+                err.note(msg);
+                return;
+            };
+            let (qself_sugg_span, is_self) =
+                if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
+                    // If the path segment already has type params, we want to overwrite
+                    // them.
+                    match &path.segments {
+                        // `segment` is the previous to last element on the path,
+                        // which would normally be the `enum` itself, while the last
+                        // `_` `PathSegment` corresponds to the variant.
+                        [
+                            ..,
+                            hir::PathSegment {
+                                ident, args, res: Res::Def(DefKind::Enum, _), ..
+                            },
+                            _,
+                        ] => (
+                            // We need to include the `::` in `Type::Variant::`
+                            // to point the span to `::`, not just ``.
+                            ident
+                                .span
+                                .shrink_to_hi()
+                                .to(args.map_or(ident.span.shrink_to_hi(), |a| a.span_ext)),
+                            false,
+                        ),
+                        [segment] => {
+                            (
+                                // We need to include the `::` in `Type::Variant::`
+                                // to point the span to `::`, not just ``.
+                                segment.ident.span.shrink_to_hi().to(segment
+                                    .args
+                                    .map_or(segment.ident.span.shrink_to_hi(), |a| a.span_ext)),
+                                kw::SelfUpper == segment.ident.name,
+                            )
+                        }
+                        _ => {
+                            err.note(msg);
+                            return;
+                        }
+                    }
+                } else {
+                    err.note(msg);
+                    return;
+                };
+            let suggestion = vec![
+                if is_self {
+                    // Account for people writing `Self::Variant::`, where
+                    // `Self` is the enum, and suggest replacing `Self` with the
+                    // appropriate type: `Type::::Variant`.
+                    (qself.span, format!("{type_name}{snippet}"))
+                } else {
+                    (qself_sugg_span, snippet)
+                },
+                (args_span, String::new()),
+            ];
+            err.multipart_suggestion_verbose(msg, suggestion, Applicability::MaybeIncorrect);
+        }
+        GenericsArgsErrExtend::PrimTy(prim_ty) => {
+            let name = prim_ty.name_str();
+            for segment in segments {
+                if let Some(args) = segment.args {
+                    err.span_suggestion_verbose(
+                        segment.ident.span.shrink_to_hi().to(args.span_ext),
+                        format!("primitive type `{name}` doesn't have generic parameters"),
+                        "",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+        GenericsArgsErrExtend::OpaqueTy => {
+            err.note("`impl Trait` types can't have type parameters");
+        }
+        GenericsArgsErrExtend::DefVariant => {
+            err.note("enum variants can't have type parameters");
+        }
+        GenericsArgsErrExtend::TyParam(def_id) => {
+            if let Some(span) = tcx.def_ident_span(def_id) {
+                let name = tcx.item_name(def_id);
+                err.span_note(span, format!("type parameter `{name}` defined here"));
+            }
+        }
+        GenericsArgsErrExtend::SelfTyParam(span) => {
+            err.span_suggestion_verbose(
+                span,
+                "the `Self` type doesn't accept type parameters",
+                "",
+                Applicability::MaybeIncorrect,
+            );
+        }
+        GenericsArgsErrExtend::SelfTyAlias { def_id, span } => {
+            let ty = tcx.at(span).type_of(def_id).instantiate_identity();
+            let span_of_impl = tcx.span_of_impl(def_id);
+            let def_id = match *ty.kind() {
+                ty::Adt(self_def, _) => self_def.did(),
+                _ => return,
+            };
+
+            let type_name = tcx.item_name(def_id);
+            let span_of_ty = tcx.def_ident_span(def_id);
+            let generics = tcx.generics_of(def_id).count();
+
+            let msg = format!("`Self` is of type `{ty}`");
+            if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) {
+                let mut span: MultiSpan = vec![t_sp].into();
+                span.push_span_label(
+                    i_sp,
+                    format!("`Self` is on type `{type_name}` in this `impl`"),
+                );
+                let mut postfix = "";
+                if generics == 0 {
+                    postfix = ", which doesn't have generic parameters";
+                }
+                span.push_span_label(t_sp, format!("`Self` corresponds to this type{postfix}"));
+                err.span_note(span, msg);
+            } else {
+                err.note(msg);
+            }
+            for segment in segments {
+                if let Some(args) = segment.args
+                    && segment.ident.name == kw::SelfUpper
+                {
+                    if generics == 0 {
+                        // FIXME(estebank): we could also verify that the arguments being
+                        // work for the `enum`, instead of just looking if it takes *any*.
+                        err.span_suggestion_verbose(
+                            segment.ident.span.shrink_to_hi().to(args.span_ext),
+                            "the `Self` type doesn't accept type parameters",
+                            "",
+                            Applicability::MachineApplicable,
+                        );
+                        return;
+                    } else {
+                        err.span_suggestion_verbose(
+                            segment.ident.span,
+                            format!(
+                                "the `Self` type doesn't accept type parameters, use the \
+                                concrete type's name `{type_name}` instead if you want to \
+                                specify its type parameters"
+                            ),
+                            type_name,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+        _ => {}
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 8886a78c6ecd..f726f2a7b890 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -14,7 +14,7 @@
 //! trait references and bounds.
 
 mod bounds;
-mod errors;
+pub mod errors;
 pub mod generics;
 mod lint;
 mod object_safety;
@@ -22,14 +22,14 @@ mod object_safety;
 use crate::bounds::Bounds;
 use crate::collect::HirPlaceholderCollector;
 use crate::errors::AmbiguousLifetimeBound;
-use crate::hir_ty_lowering::errors::prohibit_assoc_item_binding;
+use crate::hir_ty_lowering::errors::{prohibit_assoc_item_binding, GenericsArgsErrExtend};
 use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
 use crate::middle::resolve_bound_vars as rbv;
 use crate::require_c_abi_if_c_variadic;
 use rustc_ast::TraitObjectSyntax;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::{
-    codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, MultiSpan,
+    codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
@@ -46,7 +46,7 @@ use rustc_middle::ty::{
 use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{sym, BytePos, Span, DUMMY_SP};
+use rustc_span::{sym, Span, DUMMY_SP};
 use rustc_target::spec::abi;
 use rustc_trait_selection::traits::wf::object_region_bounds;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
@@ -632,7 +632,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_ref: &hir::TraitRef<'tcx>,
         self_ty: Ty<'tcx>,
     ) -> ty::TraitRef<'tcx> {
-        self.prohibit_generic_args(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
+        let _ = self.prohibit_generic_args(
+            trait_ref.path.segments.split_last().unwrap().1.iter(),
+            GenericsArgsErrExtend::None,
+        );
 
         self.lower_mono_trait_ref(
             trait_ref.path.span,
@@ -681,7 +684,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());
         let trait_segment = trait_ref.path.segments.last().unwrap();
 
-        self.prohibit_generic_args(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
+        let _ = self.prohibit_generic_args(
+            trait_ref.path.segments.split_last().unwrap().1.iter(),
+            GenericsArgsErrExtend::None,
+        );
         self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
 
         let (generic_args, arg_count) = self.lower_generic_args_of_path(
@@ -995,8 +1001,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         hir_ref_id: hir::HirId,
         span: Span,
         qself_ty: Ty<'tcx>,
-        qself: &hir::Ty<'_>,
-        assoc_segment: &hir::PathSegment<'tcx>,
+        qself: &'tcx hir::Ty<'tcx>,
+        assoc_segment: &'tcx hir::PathSegment<'tcx>,
         permit_variants: bool,
     ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
         debug!(%qself_ty, ?assoc_segment.ident);
@@ -1020,99 +1026,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 if let Some(variant_def) = variant_def {
                     if permit_variants {
                         tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
-                        self.prohibit_generic_args(slice::from_ref(assoc_segment).iter(), |err| {
-                            err.note("enum variants can't have type parameters");
-                            let type_name = tcx.item_name(adt_def.did());
-                            let msg = format!(
-                                "you might have meant to specify type parameters on enum \
-                                 `{type_name}`"
-                            );
-                            let Some(args) = assoc_segment.args else {
-                                return;
-                            };
-                            // Get the span of the generics args *including* the leading `::`.
-                            // We do so by stretching args.span_ext to the left by 2. Earlier
-                            // it was done based on the end of assoc segment but that sometimes
-                            // led to impossible spans and caused issues like #116473
-                            let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2));
-                            if tcx.generics_of(adt_def.did()).count() == 0 {
-                                // FIXME(estebank): we could also verify that the arguments being
-                                // work for the `enum`, instead of just looking if it takes *any*.
-                                err.span_suggestion_verbose(
-                                    args_span,
-                                    format!("{type_name} doesn't have generic parameters"),
-                                    "",
-                                    Applicability::MachineApplicable,
-                                );
-                                return;
-                            }
-                            let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span)
-                            else {
-                                err.note(msg);
-                                return;
-                            };
-                            let (qself_sugg_span, is_self) =
-                                if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) =
-                                    &qself.kind
-                                {
-                                    // If the path segment already has type params, we want to overwrite
-                                    // them.
-                                    match &path.segments {
-                                        // `segment` is the previous to last element on the path,
-                                        // which would normally be the `enum` itself, while the last
-                                        // `_` `PathSegment` corresponds to the variant.
-                                        [
-                                            ..,
-                                            hir::PathSegment {
-                                                ident,
-                                                args,
-                                                res: Res::Def(DefKind::Enum, _),
-                                                ..
-                                            },
-                                            _,
-                                        ] => (
-                                            // We need to include the `::` in `Type::Variant::`
-                                            // to point the span to `::`, not just ``.
-                                            ident.span.shrink_to_hi().to(args
-                                                .map_or(ident.span.shrink_to_hi(), |a| a.span_ext)),
-                                            false,
-                                        ),
-                                        [segment] => (
-                                            // We need to include the `::` in `Type::Variant::`
-                                            // to point the span to `::`, not just ``.
-                                            segment.ident.span.shrink_to_hi().to(segment
-                                                .args
-                                                .map_or(segment.ident.span.shrink_to_hi(), |a| {
-                                                    a.span_ext
-                                                })),
-                                            kw::SelfUpper == segment.ident.name,
-                                        ),
-                                        _ => {
-                                            err.note(msg);
-                                            return;
-                                        }
-                                    }
-                                } else {
-                                    err.note(msg);
-                                    return;
-                                };
-                            let suggestion = vec![
-                                if is_self {
-                                    // Account for people writing `Self::Variant::`, where
-                                    // `Self` is the enum, and suggest replacing `Self` with the
-                                    // appropriate type: `Type::::Variant`.
-                                    (qself.span, format!("{type_name}{snippet}"))
-                                } else {
-                                    (qself_sugg_span, snippet)
-                                },
-                                (args_span, String::new()),
-                            ];
-                            err.multipart_suggestion_verbose(
-                                msg,
-                                suggestion,
-                                Applicability::MaybeIncorrect,
-                            );
-                        });
+                        let _ = self.prohibit_generic_args(
+                            slice::from_ref(assoc_segment).iter(),
+                            GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def },
+                        );
                         return Ok((qself_ty, DefKind::Variant, variant_def.def_id));
                     } else {
                         variant_resolution = Some(variant_def.def_id);
@@ -1624,111 +1541,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     pub fn prohibit_generic_args<'a>(
         &self,
         segments: impl Iterator> + Clone,
-        extend: impl Fn(&mut Diag<'_>),
-    ) -> bool {
-        let args = segments.clone().flat_map(|segment| segment.args().args);
-
-        let (lt, ty, ct, inf) =
-            args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg {
-                hir::GenericArg::Lifetime(_) => (true, ty, ct, inf),
-                hir::GenericArg::Type(_) => (lt, true, ct, inf),
-                hir::GenericArg::Const(_) => (lt, ty, true, inf),
-                hir::GenericArg::Infer(_) => (lt, ty, ct, true),
-            });
-        let mut emitted = false;
-        if lt || ty || ct || inf {
-            let types_and_spans: Vec<_> = segments
-                .clone()
-                .flat_map(|segment| {
-                    if segment.args().args.is_empty() {
-                        None
-                    } else {
-                        Some((
-                            match segment.res {
-                                Res::PrimTy(ty) => {
-                                    format!("{} `{}`", segment.res.descr(), ty.name())
-                                }
-                                Res::Def(_, def_id)
-                                    if let Some(name) = self.tcx().opt_item_name(def_id) =>
-                                {
-                                    format!("{} `{name}`", segment.res.descr())
-                                }
-                                Res::Err => "this type".to_string(),
-                                _ => segment.res.descr().to_string(),
-                            },
-                            segment.ident.span,
-                        ))
-                    }
-                })
-                .collect();
-            let this_type = match &types_and_spans[..] {
-                [.., _, (last, _)] => format!(
-                    "{} and {last}",
-                    types_and_spans[..types_and_spans.len() - 1]
-                        .iter()
-                        .map(|(x, _)| x.as_str())
-                        .intersperse(", ")
-                        .collect::()
-                ),
-                [(only, _)] => only.to_string(),
-                [] => "this type".to_string(),
-            };
-
-            let arg_spans: Vec = args.map(|arg| arg.span()).collect();
-
-            let mut kinds = Vec::with_capacity(4);
-            if lt {
-                kinds.push("lifetime");
-            }
-            if ty {
-                kinds.push("type");
-            }
-            if ct {
-                kinds.push("const");
-            }
-            if inf {
-                kinds.push("generic");
-            }
-            let (kind, s) = match kinds[..] {
-                [.., _, last] => (
-                    format!(
-                        "{} and {last}",
-                        kinds[..kinds.len() - 1]
-                            .iter()
-                            .map(|&x| x)
-                            .intersperse(", ")
-                            .collect::()
-                    ),
-                    "s",
-                ),
-                [only] => (only.to_string(), ""),
-                [] => unreachable!("expected at least one generic to prohibit"),
-            };
-            let last_span = *arg_spans.last().unwrap();
-            let span: MultiSpan = arg_spans.into();
-            let mut err = struct_span_code_err!(
-                self.tcx().dcx(),
-                span,
-                E0109,
-                "{kind} arguments are not allowed on {this_type}",
-            );
-            err.span_label(last_span, format!("{kind} argument{s} not allowed"));
-            for (what, span) in types_and_spans {
-                err.span_label(span, format!("not allowed on {what}"));
-            }
-            extend(&mut err);
-            self.set_tainted_by_errors(err.emit());
-            emitted = true;
+        err_extend: GenericsArgsErrExtend<'_>,
+    ) -> Result<(), ErrorGuaranteed> {
+        let args_visitors = segments.clone().flat_map(|segment| segment.args().args);
+        let mut result = Ok(());
+        if let Some(_) = args_visitors.clone().next() {
+            result = Err(self.report_prohibit_generics_error(
+                segments.clone(),
+                args_visitors,
+                err_extend,
+            ));
         }
 
         for segment in segments {
             // Only emit the first error to avoid overloading the user with error messages.
             if let Some(b) = segment.args().bindings.first() {
-                prohibit_assoc_item_binding(self.tcx(), b.span, None);
-                return true;
+                return Err(prohibit_assoc_item_binding(self.tcx(), b.span, None));
             }
         }
-        emitted
+
+        result
     }
 
     /// Probe path segments that are semantically allowed to have generic arguments.
@@ -1893,9 +1725,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 // Check for desugared `impl Trait`.
                 assert!(tcx.is_type_alias_impl_trait(did));
                 let item_segment = path.segments.split_last().unwrap();
-                self.prohibit_generic_args(item_segment.1.iter(), |err| {
-                    err.note("`impl Trait` types can't have type parameters");
-                });
+                let _ = self
+                    .prohibit_generic_args(item_segment.1.iter(), GenericsArgsErrExtend::OpaqueTy);
                 let args = self.lower_generic_args_of_path_segment(span, did, item_segment.0);
                 Ty::new_opaque(tcx, did, args)
             }
@@ -1908,7 +1739,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 did,
             ) => {
                 assert_eq!(opt_self_ty, None);
-                self.prohibit_generic_args(path.segments.split_last().unwrap().1.iter(), |_| {});
+                let _ = self.prohibit_generic_args(
+                    path.segments.split_last().unwrap().1.iter(),
+                    GenericsArgsErrExtend::None,
+                );
                 self.lower_path_segment(span, did, path.segments.last().unwrap())
             }
             Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => {
@@ -1920,13 +1754,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     self.probe_generic_path_segments(path.segments, None, kind, def_id, span);
                 let indices: FxHashSet<_> =
                     generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect();
-                self.prohibit_generic_args(
+                let _ = self.prohibit_generic_args(
                     path.segments.iter().enumerate().filter_map(|(index, seg)| {
                         if !indices.contains(&index) { Some(seg) } else { None }
                     }),
-                    |err| {
-                        err.note("enum variants can't have type parameters");
-                    },
+                    GenericsArgsErrExtend::DefVariant,
                 );
 
                 let GenericPathSegment(def_id, index) = generic_segments.last().unwrap();
@@ -1934,27 +1766,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
             Res::Def(DefKind::TyParam, def_id) => {
                 assert_eq!(opt_self_ty, None);
-                self.prohibit_generic_args(path.segments.iter(), |err| {
-                    if let Some(span) = tcx.def_ident_span(def_id) {
-                        let name = tcx.item_name(def_id);
-                        err.span_note(span, format!("type parameter `{name}` defined here"));
-                    }
-                });
+                let _ = self.prohibit_generic_args(
+                    path.segments.iter(),
+                    GenericsArgsErrExtend::TyParam(def_id),
+                );
                 self.lower_ty_param(hir_id)
             }
             Res::SelfTyParam { .. } => {
                 // `Self` in trait or type alias.
                 assert_eq!(opt_self_ty, None);
-                self.prohibit_generic_args(path.segments.iter(), |err| {
+                let _ = self.prohibit_generic_args(
+                    path.segments.iter(),
                     if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments {
-                        err.span_suggestion_verbose(
+                        GenericsArgsErrExtend::SelfTyParam(
                             ident.span.shrink_to_hi().to(args.span_ext),
-                            "the `Self` type doesn't accept type parameters",
-                            "",
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                });
+                        )
+                    } else {
+                        GenericsArgsErrExtend::None
+                    },
+                );
                 tcx.types.self_param
             }
             Res::SelfTyAlias { alias_to: def_id, forbid_generic, .. } => {
@@ -1962,65 +1792,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 assert_eq!(opt_self_ty, None);
                 // Try to evaluate any array length constants.
                 let ty = tcx.at(span).type_of(def_id).instantiate_identity();
-                let span_of_impl = tcx.span_of_impl(def_id);
-                self.prohibit_generic_args(path.segments.iter(), |err| {
-                    let def_id = match *ty.kind() {
-                        ty::Adt(self_def, _) => self_def.did(),
-                        _ => return,
-                    };
-
-                    let type_name = tcx.item_name(def_id);
-                    let span_of_ty = tcx.def_ident_span(def_id);
-                    let generics = tcx.generics_of(def_id).count();
-
-                    let msg = format!("`Self` is of type `{ty}`");
-                    if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) {
-                        let mut span: MultiSpan = vec![t_sp].into();
-                        span.push_span_label(
-                            i_sp,
-                            format!("`Self` is on type `{type_name}` in this `impl`"),
-                        );
-                        let mut postfix = "";
-                        if generics == 0 {
-                            postfix = ", which doesn't have generic parameters";
-                        }
-                        span.push_span_label(
-                            t_sp,
-                            format!("`Self` corresponds to this type{postfix}"),
-                        );
-                        err.span_note(span, msg);
-                    } else {
-                        err.note(msg);
-                    }
-                    for segment in path.segments {
-                        if let Some(args) = segment.args
-                            && segment.ident.name == kw::SelfUpper
-                        {
-                            if generics == 0 {
-                                // FIXME(estebank): we could also verify that the arguments being
-                                // work for the `enum`, instead of just looking if it takes *any*.
-                                err.span_suggestion_verbose(
-                                    segment.ident.span.shrink_to_hi().to(args.span_ext),
-                                    "the `Self` type doesn't accept type parameters",
-                                    "",
-                                    Applicability::MachineApplicable,
-                                );
-                                return;
-                            } else {
-                                err.span_suggestion_verbose(
-                                    segment.ident.span,
-                                    format!(
-                                        "the `Self` type doesn't accept type parameters, use the \
-                                        concrete type's name `{type_name}` instead if you want to \
-                                        specify its type parameters"
-                                    ),
-                                    type_name,
-                                    Applicability::MaybeIncorrect,
-                                );
-                            }
-                        }
-                    }
-                });
+                let _ = self.prohibit_generic_args(
+                    path.segments.iter(),
+                    GenericsArgsErrExtend::SelfTyAlias { def_id, span },
+                );
                 // HACK(min_const_generics): Forbid generic `Self` types
                 // here as we can't easily do that during nameres.
                 //
@@ -2061,7 +1836,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
             Res::Def(DefKind::AssocTy, def_id) => {
                 debug_assert!(path.segments.len() >= 2);
-                self.prohibit_generic_args(path.segments[..path.segments.len() - 2].iter(), |_| {});
+                let _ = self.prohibit_generic_args(
+                    path.segments[..path.segments.len() - 2].iter(),
+                    GenericsArgsErrExtend::None,
+                );
                 // HACK: until we support ``, assume all of them are.
                 let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) {
                     ty::BoundConstness::ConstIfConst
@@ -2079,19 +1857,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
             Res::PrimTy(prim_ty) => {
                 assert_eq!(opt_self_ty, None);
-                self.prohibit_generic_args(path.segments.iter(), |err| {
-                    let name = prim_ty.name_str();
-                    for segment in path.segments {
-                        if let Some(args) = segment.args {
-                            err.span_suggestion_verbose(
-                                segment.ident.span.shrink_to_hi().to(args.span_ext),
-                                format!("primitive type `{name}` doesn't have generic parameters"),
-                                "",
-                                Applicability::MaybeIncorrect,
-                            );
-                        }
-                    }
-                });
+                let _ = self.prohibit_generic_args(
+                    path.segments.iter(),
+                    GenericsArgsErrExtend::PrimTy(prim_ty),
+                );
                 match prim_ty {
                     hir::PrimTy::Bool => tcx.types.bool,
                     hir::PrimTy::Char => tcx.types.char,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
index b5b3a9131c57..d3ca35ba481e 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
@@ -1,5 +1,4 @@
 use crate::bounds::Bounds;
-use crate::errors::TraitObjectDeclaredWithNoTraits;
 use crate::hir_ty_lowering::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::{codes::*, struct_span_code_err};
@@ -86,47 +85,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) =
             expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
         if regular_traits.len() > 1 {
-            let first_trait = ®ular_traits[0];
-            let additional_trait = ®ular_traits[1];
-            let mut err = struct_span_code_err!(
-                tcx.dcx(),
-                additional_trait.bottom().1,
-                E0225,
-                "only auto traits can be used as additional traits in a trait object"
-            );
-            additional_trait.label_with_exp_info(
-                &mut err,
-                "additional non-auto trait",
-                "additional use",
-            );
-            first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
-            err.help(format!(
-                "consider creating a new trait with all of these as supertraits and using that \
-             trait here instead: `trait NewTrait: {} {{}}`",
-                regular_traits
-                    .iter()
-                    // FIXME: This should `print_sugared`, but also needs to integrate projection bounds...
-                    .map(|t| t.trait_ref().print_only_trait_path().to_string())
-                    .collect::>()
-                    .join(" + "),
-            ));
-            err.note(
-                "auto-traits like `Send` and `Sync` are traits that have special properties; \
-             for more information on them, visit \
-             ",
-            );
-            self.set_tainted_by_errors(err.emit());
-        }
-
-        if regular_traits.is_empty() && auto_traits.is_empty() {
-            let trait_alias_span = trait_bounds
-                .iter()
-                .map(|&(trait_ref, _)| trait_ref.def_id())
-                .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
-                .map(|trait_ref| tcx.def_span(trait_ref));
-            let reported =
-                tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
-            self.set_tainted_by_errors(reported);
+            let _ = self.report_trait_object_addition_traits_error(®ular_traits);
+        } else if regular_traits.is_empty() && auto_traits.is_empty() {
+            let reported = self.report_trait_object_with_no_traits_error(span, &trait_bounds);
             return Ty::new_error(tcx, reported);
         }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 011607bacc6c..8d4d0833eef6 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -11,6 +11,7 @@ use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, GenericArg, Node, QPath};
+use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend;
 use rustc_hir_analysis::hir_ty_lowering::generics::{
     check_generic_arg_count_for_call, lower_generic_args,
 };
@@ -1177,11 +1178,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let indices: FxHashSet<_> =
             generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect();
-        let generics_has_err = self.lowerer().prohibit_generic_args(
+        let generics_err = self.lowerer().prohibit_generic_args(
             segments.iter().enumerate().filter_map(|(index, seg)| {
                 if !indices.contains(&index) || is_alias_variant_ctor { Some(seg) } else { None }
             }),
-            |_| {},
+            GenericsArgsErrExtend::None,
         );
 
         if let Res::Local(hid) = res {
@@ -1191,7 +1192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return (ty, res);
         }
 
-        if generics_has_err {
+        if let Err(_) = generics_err {
             // Don't try to infer type parameters when prohibited generic arguments were given.
             user_self_ty = None;
         }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index c875d3da47e5..3ab4872fffee 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -65,7 +65,7 @@ pub use self::util::{
     check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds,
     transitive_bounds_that_define_assoc_item, SupertraitDefIds,
 };
-pub use self::util::{expand_trait_aliases, TraitAliasExpander};
+pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo};
 pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
 pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
 
diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs
index d08ca644a1c8..5c96c653df52 100644
--- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs
+++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs
@@ -2,6 +2,6 @@ fn main() {
     let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
 //~^ ERROR expected a pattern, found an expression
 //~| ERROR cannot find type `T` in this scope
-//~| ERROR type and const arguments are not allowed on builtin type `str`
+//~| ERROR const and type arguments are not allowed on builtin type `str`
 //~| ERROR expected unit struct, unit variant or constant, found associated function `str<, T>::as_bytes`
 }
diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr
index 19d4ac713ce3..d62c019a1e19 100644
--- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr
+++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr
@@ -10,11 +10,11 @@ error[E0412]: cannot find type `T` in this scope
 LL |     let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
    |                                                       ^ not found in this scope
 
-error[E0109]: type and const arguments are not allowed on builtin type `str`
+error[E0109]: const and type arguments are not allowed on builtin type `str`
   --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:15
    |
 LL |     let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
-   |         ---   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^ type and const arguments not allowed
+   |         ---   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^ const and type arguments not allowed
    |         |
    |         not allowed on builtin type `str`
    |

From 64b75f736ddaf10887a508941ab7dea9a67d01d4 Mon Sep 17 00:00:00 2001
From: Oli Scherer 
Date: Tue, 2 Apr 2024 13:00:46 +0000
Subject: [PATCH 178/192] Forbid implicit nested statics in thread local
 statics

---
 compiler/rustc_const_eval/messages.ftl        |  1 +
 compiler/rustc_const_eval/src/errors.rs       |  7 +++++
 .../rustc_const_eval/src/interpret/intern.rs  |  6 ++++-
 tests/ui/statics/nested_thread_local.rs       | 26 ++++++-------------
 tests/ui/statics/nested_thread_local.stderr   |  8 ++++++
 5 files changed, 29 insertions(+), 19 deletions(-)
 create mode 100644 tests/ui/statics/nested_thread_local.stderr

diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index d6aae60c3382..f6937dc145d5 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -222,6 +222,7 @@ const_eval_mut_deref =
 
 const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
 
+const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
 const_eval_non_const_fmt_macro_call =
     cannot call non-const formatting macro in {const_eval_const_context}s
 
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 5c46ec799f1e..a60cedd6500d 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -25,6 +25,13 @@ pub(crate) struct DanglingPtrInFinal {
     pub kind: InternKind,
 }
 
+#[derive(Diagnostic)]
+#[diag(const_eval_nested_static_in_thread_local)]
+pub(crate) struct NestedStaticInThreadLocal {
+    #[primary_span]
+    pub span: Span,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(const_eval_mutable_ptr_in_final)]
 pub(crate) struct MutablePtrInFinal {
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index f4e46c9499e2..d0f0190fea7e 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -28,7 +28,7 @@ use rustc_span::sym;
 
 use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy};
 use crate::const_eval;
-use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal};
+use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal, NestedStaticInThreadLocal};
 
 pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
         'mir,
@@ -108,6 +108,10 @@ fn intern_as_new_static<'tcx>(
     );
     tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
 
+    if tcx.is_thread_local_static(static_id.into()) {
+        tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
+    }
+
     // These do not inherit the codegen attrs of the parent static allocation, since
     // it doesn't make sense for them to inherit their `#[no_mangle]` and `#[link_name = ..]`
     // and the like.
diff --git a/tests/ui/statics/nested_thread_local.rs b/tests/ui/statics/nested_thread_local.rs
index 10e0a3420d96..a512016335a0 100644
--- a/tests/ui/statics/nested_thread_local.rs
+++ b/tests/ui/statics/nested_thread_local.rs
@@ -1,24 +1,14 @@
-// Check that nested statics in thread locals are
-// duplicated per thread.
+// Check that we forbid nested statics in `thread_local` statics.
 
 #![feature(const_refs_to_cell)]
 #![feature(thread_local)]
 
-//@run-pass
-
 #[thread_local]
-static mut FOO: &mut u32 = &mut 42;
+static mut FOO: &u32 = {
+    //~^ ERROR: does not support implicit nested statics
+    // Prevent promotion (that would trigger on `&42` as an expression)
+    let x = 42;
+    &{ x }
+};
 
-fn main() {
-    unsafe {
-        *FOO = 1;
-
-        let _ = std::thread::spawn(|| {
-            assert_eq!(*FOO, 42);
-            *FOO = 99;
-        })
-        .join();
-
-        assert_eq!(*FOO, 1);
-    }
-}
+fn main() {}
diff --git a/tests/ui/statics/nested_thread_local.stderr b/tests/ui/statics/nested_thread_local.stderr
new file mode 100644
index 000000000000..30c742626fa9
--- /dev/null
+++ b/tests/ui/statics/nested_thread_local.stderr
@@ -0,0 +1,8 @@
+error: #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
+  --> $DIR/nested_thread_local.rs:7:1
+   |
+LL | static mut FOO: &u32 = {
+   | ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+

From b4993c47f2f1348356d87c92ea54fd20fcd71cb7 Mon Sep 17 00:00:00 2001
From: Oli Scherer 
Date: Tue, 2 Apr 2024 14:10:06 +0000
Subject: [PATCH 179/192] Prefer `UnordSet` over `FxHashSet` where possible

---
 compiler/rustc_mir_transform/src/coroutine/by_move_body.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
index e0bbd582d88c..9201ad401521 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -3,7 +3,7 @@
 //! be a coroutine body that takes all of its upvars by-move, and which we stash
 //! into the `CoroutineInfo` for all coroutines returned by coroutine-closures.
 
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::unord::UnordSet;
 use rustc_hir as hir;
 use rustc_middle::mir::visit::MutVisitor;
 use rustc_middle::mir::{self, dump_mir, MirPass};
@@ -33,7 +33,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
             return;
         }
 
-        let mut by_ref_fields = FxIndexSet::default();
+        let mut by_ref_fields = UnordSet::default();
         let by_move_upvars = Ty::new_tup_from_iter(
             tcx,
             tcx.closure_captures(coroutine_def_id).iter().enumerate().map(|(idx, capture)| {
@@ -73,7 +73,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
 
 struct MakeByMoveBody<'tcx> {
     tcx: TyCtxt<'tcx>,
-    by_ref_fields: FxIndexSet,
+    by_ref_fields: UnordSet,
     by_move_coroutine_ty: Ty<'tcx>,
 }
 

From 6f3cc0903c7d540acc10ea13fa2cf6aaec8f64cd Mon Sep 17 00:00:00 2001
From: Oli Scherer 
Date: Tue, 2 Apr 2024 14:13:05 +0000
Subject: [PATCH 180/192] Avoid an `is_empty()` followed by an index op in
 favor of a single fallible op

---
 compiler/rustc_mir_transform/src/coroutine/by_move_body.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
index 9201ad401521..99dbb3422683 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -89,11 +89,11 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
         location: mir::Location,
     ) {
         if place.local == ty::CAPTURE_STRUCT_LOCAL
-            && !place.projection.is_empty()
-            && let mir::ProjectionElem::Field(idx, ty) = place.projection[0]
+            && let Some((&mir::ProjectionElem::Field(idx, ty), projection)) =
+                place.projection.split_first()
             && self.by_ref_fields.contains(&idx)
         {
-            let (begin, end) = place.projection[1..].split_first().unwrap();
+            let (begin, end) = projection.split_first().unwrap();
             // FIXME(async_closures): I'm actually a bit surprised to see that we always
             // initially deref the by-ref upvars. If this is not actually true, then we
             // will at least get an ICE that explains why this isn't true :^)

From a272007a209a4327ad8122af1ad9f9620f04724e Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Tue, 2 Apr 2024 07:55:52 -0700
Subject: [PATCH 181/192] Clean up
 src/librustdoc/html/render/search_index/encode.rs

Co-authored-by: Guillaume Gomez 
---
 .../html/render/search_index/encode.rs        | 46 +++++++++----------
 1 file changed, 22 insertions(+), 24 deletions(-)

diff --git a/src/librustdoc/html/render/search_index/encode.rs b/src/librustdoc/html/render/search_index/encode.rs
index af5eccd5bedc..54407c614c4c 100644
--- a/src/librustdoc/html/render/search_index/encode.rs
+++ b/src/librustdoc/html/render/search_index/encode.rs
@@ -92,24 +92,23 @@ impl Container {
                     r += !chunk & u64::from((chunk << 1).count_ones());
                     r += !next_chunk & u64::from((chunk >> 63).count_ones());
                 }
-                if (2 + 4 * r) < 8192 {
-                    let bits = std::mem::replace(bits, Box::new([0; 1024]));
-                    *self = Container::Run(Vec::new());
-                    for (i, bits) in bits.iter().copied().enumerate() {
-                        if bits == 0 {
-                            continue;
-                        }
-                        for j in 0..64 {
-                            let value = (u16::try_from(i).unwrap() << 6) | j;
-                            if bits & (1 << j) != 0 {
-                                self.push(value);
-                            }
+                if (2 + 4 * r) >= 8192 {
+                    return false;
+                }
+                let bits = std::mem::replace(bits, Box::new([0; 1024]));
+                *self = Container::Run(Vec::new());
+                for (i, bits) in bits.iter().copied().enumerate() {
+                    if bits == 0 {
+                        continue;
+                    }
+                    for j in 0..64 {
+                        let value = (u16::try_from(i).unwrap() << 6) | j;
+                        if bits & (1 << j) != 0 {
+                            self.push(value);
                         }
                     }
-                    true
-                } else {
-                    false
                 }
+                true
             }
             Container::Array(array) if array.len() <= 5 => false,
             Container::Array(array) => {
@@ -121,16 +120,15 @@ impl Container {
                     }
                     prev = Some(value);
                 }
-                if 2 + 4 * r < 2 * array.len() + 2 {
-                    let array = std::mem::replace(array, Vec::new());
-                    *self = Container::Run(Vec::new());
-                    for value in array {
-                        self.push(value);
-                    }
-                    true
-                } else {
-                    false
+                if 2 + 4 * r >= 2 * array.len() + 2 {
+                    return false;
                 }
+                let array = std::mem::replace(array, Vec::new());
+                *self = Container::Run(Vec::new());
+                for value in array {
+                    self.push(value);
+                }
+                true
             }
             Container::Run(_) => true,
         }

From 858a1dfd5bc7c27655d07d45c792fccb4ac0c43d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= 
Date: Tue, 2 Apr 2024 18:02:06 +0200
Subject: [PATCH 182/192] Remove dangling `.mir.stderr` and `.thir.stderr` test
 files

---
 ...const-extern-fn-requires-unsafe.mir.stderr |  19 ---
 ...onst-extern-fn-requires-unsafe.thir.stderr |  19 ---
 .../fn-ptr.mir.stderr                         |  23 ----
 .../fn-ptr.thir.stderr                        |  23 ----
 .../safe-calls.mir.stderr                     | 115 ------------------
 .../safe-calls.thir.stderr                    | 115 ------------------
 6 files changed, 314 deletions(-)
 delete mode 100644 tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr
 delete mode 100644 tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr
 delete mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr
 delete mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr
 delete mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
 delete mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr

diff --git a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr b/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr
deleted file mode 100644
index 34ec8aadbcf3..000000000000
--- a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/const-extern-fn-requires-unsafe.rs:12:5
-   |
-LL |     foo();
-   |     ^^^^^ call to unsafe function
-   |
-   = note: consult the function's documentation for information on how to avoid undefined behavior
-
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/const-extern-fn-requires-unsafe.rs:9:17
-   |
-LL |     let a: [u8; foo()];
-   |                 ^^^^^ call to unsafe function
-   |
-   = note: consult the function's documentation for information on how to avoid undefined behavior
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr b/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr
deleted file mode 100644
index e6b8173eb051..000000000000
--- a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block
-  --> $DIR/const-extern-fn-requires-unsafe.rs:12:5
-   |
-LL |     foo();
-   |     ^^^^^ call to unsafe function
-   |
-   = note: consult the function's documentation for information on how to avoid undefined behavior
-
-error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block
-  --> $DIR/const-extern-fn-requires-unsafe.rs:9:17
-   |
-LL |     let a: [u8; foo()];
-   |                 ^^^^^ call to unsafe function
-   |
-   = note: consult the function's documentation for information on how to avoid undefined behavior
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr
deleted file mode 100644
index 7bbd4e158982..000000000000
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/fn-ptr.rs:11:21
-   |
-LL | #[target_feature(enable = "sse2")]
-   | ---------------------------------- `#[target_feature]` added here
-...
-LL |     let foo: fn() = foo;
-   |              ----   ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
-   |              |
-   |              expected due to this
-   |
-   = note: expected fn pointer `fn()`
-                 found fn item `fn() {foo}`
-   = note: fn items are distinct from fn pointers
-   = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
-help: consider casting to a fn pointer
-   |
-LL |     let foo: fn() = foo as fn();
-   |                     ~~~~~~~~~~~
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr
deleted file mode 100644
index 7bbd4e158982..000000000000
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/fn-ptr.rs:11:21
-   |
-LL | #[target_feature(enable = "sse2")]
-   | ---------------------------------- `#[target_feature]` added here
-...
-LL |     let foo: fn() = foo;
-   |              ----   ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
-   |              |
-   |              expected due to this
-   |
-   = note: expected fn pointer `fn()`
-                 found fn item `fn() {foo}`
-   = note: fn items are distinct from fn pointers
-   = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
-help: consider casting to a fn pointer
-   |
-LL |     let foo: fn() = foo as fn();
-   |                     ~~~~~~~~~~~
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
deleted file mode 100644
index cabc475fa61c..000000000000
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
+++ /dev/null
@@ -1,115 +0,0 @@
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:28:5
-   |
-LL |     sse2();
-   |     ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:31:5
-   |
-LL |     avx_bmi2();
-   |     ^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:34:5
-   |
-LL |     Quux.avx_bmi2();
-   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:41:5
-   |
-LL |     avx_bmi2();
-   |     ^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:44:5
-   |
-LL |     Quux.avx_bmi2();
-   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:51:5
-   |
-LL |     sse2();
-   |     ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:54:5
-   |
-LL |     avx_bmi2();
-   |     ^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: bmi2
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:57:5
-   |
-LL |     Quux.avx_bmi2();
-   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: bmi2
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:65:5
-   |
-LL |     sse2();
-   |     ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:70:15
-   |
-LL | const _: () = sse2();
-   |               ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:74:15
-   |
-LL | const _: () = sse2_and_fxsr();
-   |               ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr
-   = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
-
-error: call to function with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
-  --> $DIR/safe-calls.rs:82:5
-   |
-LL |     sse2();
-   |     ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-note: an unsafe function restricts its caller, but its body is safe by default
-  --> $DIR/safe-calls.rs:81:1
-   |
-LL | unsafe fn needs_unsafe_block() {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: the lint level is defined here
-  --> $DIR/safe-calls.rs:78:8
-   |
-LL | #[deny(unsafe_op_in_unsafe_fn)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 12 previous errors
-
-For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr
deleted file mode 100644
index 13b58fde862d..000000000000
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr
+++ /dev/null
@@ -1,115 +0,0 @@
-error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:28:5
-   |
-LL |     sse2();
-   |     ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-
-error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:31:5
-   |
-LL |     avx_bmi2();
-   |     ^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
-
-error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:34:5
-   |
-LL |     Quux.avx_bmi2();
-   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
-
-error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:41:5
-   |
-LL |     avx_bmi2();
-   |     ^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
-
-error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:44:5
-   |
-LL |     Quux.avx_bmi2();
-   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
-
-error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:51:5
-   |
-LL |     sse2();
-   |     ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-
-error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:54:5
-   |
-LL |     avx_bmi2();
-   |     ^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: bmi2
-
-error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:57:5
-   |
-LL |     Quux.avx_bmi2();
-   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: bmi2
-
-error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:65:5
-   |
-LL |     sse2();
-   |     ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-
-error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:70:15
-   |
-LL | const _: () = sse2();
-   |               ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-
-error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:74:15
-   |
-LL | const _: () = sse2_and_fxsr();
-   |               ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr
-   = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
-
-error: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
-  --> $DIR/safe-calls.rs:82:5
-   |
-LL |     sse2();
-   |     ^^^^^^ call to function with `#[target_feature]`
-   |
-   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
-   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
-note: an unsafe function restricts its caller, but its body is safe by default
-  --> $DIR/safe-calls.rs:81:1
-   |
-LL | unsafe fn needs_unsafe_block() {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: the lint level is defined here
-  --> $DIR/safe-calls.rs:78:8
-   |
-LL | #[deny(unsafe_op_in_unsafe_fn)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 12 previous errors
-
-For more information about this error, try `rustc --explain E0133`.

From 5ee4d13709c6de71c206529563e2736cd6ac0033 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= 
Date: Tue, 2 Apr 2024 18:37:01 +0200
Subject: [PATCH 183/192] rustdoc: add a couple of regression tests

---
 .../const-in-super-trait-and-item-bound.rs    | 23 +++++++++++++++++
 ...ctions-in-super-trait-bound-unsatisfied.rs | 18 +++++++++++++
 ...ns-in-super-trait-bound-unsatisfied.stderr | 25 +++++++++++++++++++
 .../unconstrained-param-in-impl-ambiguity.rs  | 10 ++++++++
 ...constrained-param-in-impl-ambiguity.stderr |  9 +++++++
 5 files changed, 85 insertions(+)
 create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs
 create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs
 create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr
 create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs
 create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr

diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs
new file mode 100644
index 000000000000..df6de6769d57
--- /dev/null
+++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs
@@ -0,0 +1,23 @@
+// We used to ICE here while trying to synthesize auto trait impls.
+// issue: 107715
+//@ check-pass
+
+pub const N: usize = 1;
+
+pub struct MapType, V> {
+    _array: K::Array,
+}
+
+pub trait Subtrait: Supertrait<[u8; N]> {}
+
+pub trait Supertrait {
+    type Array: AnotherTrait;
+}
+
+pub trait AnotherTrait {
+    const LENGTH: usize;
+}
+
+pub struct Container {
+    _x: MapType,
+}
diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs
new file mode 100644
index 000000000000..f62f8396e991
--- /dev/null
+++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs
@@ -0,0 +1,18 @@
+// We used to ICE here while trying to synthesize auto trait impls.
+// issue: 114657
+
+pub trait Foo {
+    type FooType;
+}
+
+pub trait Bar: Foo>::BarType> {
+    type BarType;
+}
+
+pub(crate) const B: usize = 5;
+
+pub trait Tec: Bar {}
+
+pub struct Structure { //~ ERROR the trait bound `C: Bar<5>` is not satisfied
+    _field: C::BarType, //~ ERROR the trait bound `C: Bar<5>` is not satisfied
+}
diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr
new file mode 100644
index 000000000000..d87e769b5053
--- /dev/null
+++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr
@@ -0,0 +1,25 @@
+error[E0277]: the trait bound `C: Bar<5>` is not satisfied
+  --> $DIR/projections-in-super-trait-bound-unsatisfied.rs:16:1
+   |
+LL | pub struct Structure {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar<5>` is not implemented for `C`
+   |
+help: consider further restricting this bound
+   |
+LL | pub struct Structure> {
+   |                             ++++++++
+
+error[E0277]: the trait bound `C: Bar<5>` is not satisfied
+  --> $DIR/projections-in-super-trait-bound-unsatisfied.rs:17:13
+   |
+LL |     _field: C::BarType,
+   |             ^^^^^^^^^^ the trait `Bar<5>` is not implemented for `C`
+   |
+help: consider further restricting this bound
+   |
+LL | pub struct Structure> {
+   |                             ++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs
new file mode 100644
index 000000000000..6c62415e06d5
--- /dev/null
+++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs
@@ -0,0 +1,10 @@
+// We used to ICE here while trying to synthesize auto trait impls.
+// issue: 112828
+
+struct Outer(Inner);
+struct Inner;
+
+unsafe impl Send for Inner {}
+//~^ ERROR the type parameter `Q` is not constrained by the impl trait, self type, or predicates
+
+trait Trait {}
diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr
new file mode 100644
index 000000000000..38d1a537fe45
--- /dev/null
+++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr
@@ -0,0 +1,9 @@
+error[E0207]: the type parameter `Q` is not constrained by the impl trait, self type, or predicates
+  --> $DIR/unconstrained-param-in-impl-ambiguity.rs:7:13
+   |
+LL | unsafe impl Send for Inner {}
+   |             ^ unconstrained type parameter
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0207`.

From 70b4ace09d5b1033c751654b9bbe714eeb4d5b4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= 
Date: Tue, 2 Apr 2024 18:59:17 +0200
Subject: [PATCH 184/192] rustdoc: synthetic auto trait impls: accept
 unresolved region vars for now

---
 src/librustdoc/clean/auto_trait.rs              |  8 +++++++-
 .../lifetime-generic-user-impl-normalize.rs     | 17 +++++++++++++++++
 .../lifetime-generic-user-impl.rs               | 11 +++++++++++
 3 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs
 create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs

diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 08c186c9d38a..217f6bb550bc 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -181,8 +181,14 @@ fn clean_param_env<'tcx>(
         })
         .map(|pred| {
             tcx.fold_regions(pred, |r, _| match *r {
-                ty::ReVar(vid) => vid_to_region[&vid],
+                // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that
+                // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds
+                // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests.
+                // This is in dire need of an investigation into `AutoTraitFinder`.
+                ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r),
                 ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r,
+                // FIXME(#120606): `AutoTraitFinder` can actually leak placeholder regions which feels
+                // incorrect. Needs investigation.
                 ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => {
                     bug!("unexpected region kind: {r:?}")
                 }
diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs
new file mode 100644
index 000000000000..1b67c2bc8756
--- /dev/null
+++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs
@@ -0,0 +1,17 @@
+// We used to ICE here while trying to synthesize auto trait impls.
+// issue: 112242
+//@ check-pass
+//@ compile-flags: -Znormalize-docs
+
+pub trait MyTrait<'a> {
+    type MyItem;
+}
+pub struct Inner(Q);
+pub struct Outer(Inner);
+
+impl<'a, Q> std::marker::Unpin for Inner
+where
+    Q: MyTrait<'a>,
+    >::MyItem: Copy,
+{
+}
diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs
new file mode 100644
index 000000000000..31d1b11ff310
--- /dev/null
+++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs
@@ -0,0 +1,11 @@
+// We used to ICE here while trying to synthesize auto trait impls.
+// issue: 123370
+//@ check-pass
+
+pub struct Inner<'a, Q>(&'a (), Q);
+
+pub struct Outer<'a, Q>(Inner<'a, Q>);
+
+impl<'a, Q: Trait<'a>> std::marker::Unpin for Inner<'static, Q> {}
+
+pub trait Trait<'a> {}

From 327aa199dd17d31db2f3d8ed80d15a394101d6b5 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Tue, 2 Apr 2024 10:17:21 -0700
Subject: [PATCH 185/192] Improve the `build_shift_expr_rhs` comment

---
 compiler/rustc_codegen_ssa/src/base.rs | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index ec21a7cee477..410b5d27c577 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -300,12 +300,15 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     }
 }
 
+/// Returns `rhs` sufficiently masked, truncated, and/or extended so that
+/// it can be used to shift `lhs`.
+///
 /// Shifts in MIR are all allowed to have mismatched LHS & RHS types.
+/// The shift methods in `BuilderMethods`, however, are fully homogeneous
+/// (both parameters and the return type are all the same type).
 ///
-/// This does all the appropriate conversions needed to pass it to the builder's
-/// shift methods, which are UB for out-of-range shifts.
-///
-/// If `is_unchecked` is false, this masks the RHS to ensure it stays in-bounds.
+/// If `is_unchecked` is false, this masks the RHS to ensure it stays in-bounds,
+/// as the `BuilderMethods` shifts are UB for out-of-bounds shift amounts.
 /// For 32- and 64-bit types, this matches the semantics
 /// of Java. (See related discussion on #1877 and #10183.)
 ///

From 46265218319573cb9afcce0c2e8dc7411ab28768 Mon Sep 17 00:00:00 2001
From: scottmcm 
Date: Tue, 2 Apr 2024 17:21:20 +0000
Subject: [PATCH 186/192] Update tests/mir-opt/inline/unchecked_shifts.rs

Co-authored-by: Waffle Maybe 
---
 tests/mir-opt/inline/unchecked_shifts.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/mir-opt/inline/unchecked_shifts.rs b/tests/mir-opt/inline/unchecked_shifts.rs
index 1e54f28725d8..3c4e73bf7ea6 100644
--- a/tests/mir-opt/inline/unchecked_shifts.rs
+++ b/tests/mir-opt/inline/unchecked_shifts.rs
@@ -5,7 +5,7 @@
 //@ compile-flags: -Zmir-opt-level=2 -Zinline-mir
 
 // These used to be more interesting when the library had to fix the RHS type.
-// After MCP#693, though, that's the backend's probablem, not something in MIR.
+// After MCP#693, though, that's the backend's problem, not something in MIR.
 
 // EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.diff
 // EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.mir

From a333b82d04c6077d639c664d45d051a0bdcbabed Mon Sep 17 00:00:00 2001
From: Matthew Maurer 
Date: Tue, 2 Apr 2024 15:17:33 +0000
Subject: [PATCH 187/192] CFI: Support non-general coroutines

Previously, we assumed all `ty::Coroutine` were general coroutines and
attempted to generalize them through the `Coroutine` trait. Select
appropriate traits for each kind of coroutine.
---
 .../src/typeid/typeid_itanium_cxx_abi.rs      | 33 ++++++++++-----
 tests/ui/sanitizer/cfi-coroutine.rs           | 41 ++++++++++++++++++-
 2 files changed, 62 insertions(+), 12 deletions(-)

diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
index 5963bd7c5f16..5f5d90f359a2 100644
--- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -1218,22 +1218,35 @@ pub fn typeid_for_instance<'tcx>(
                     let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
                     let tuple_args =
                         tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0];
-                    (trait_id, tuple_args)
+                    (trait_id, Some(tuple_args))
                 }
-                ty::Coroutine(..) => (
-                    tcx.require_lang_item(LangItem::Coroutine, None),
-                    instance.args.as_coroutine().resume_ty(),
-                ),
+                ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() {
+                    hir::CoroutineKind::Coroutine(..) => (
+                        tcx.require_lang_item(LangItem::Coroutine, None),
+                        Some(instance.args.as_coroutine().resume_ty()),
+                    ),
+                    hir::CoroutineKind::Desugared(desugaring, _) => {
+                        let lang_item = match desugaring {
+                            hir::CoroutineDesugaring::Async => LangItem::Future,
+                            hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator,
+                            hir::CoroutineDesugaring::Gen => LangItem::Iterator,
+                        };
+                        (tcx.require_lang_item(lang_item, None), None)
+                    }
+                },
                 ty::CoroutineClosure(..) => (
                     tcx.require_lang_item(LangItem::FnOnce, None),
-                    tcx.instantiate_bound_regions_with_erased(
-                        instance.args.as_coroutine_closure().coroutine_closure_sig(),
-                    )
-                    .tupled_inputs_ty,
+                    Some(
+                        tcx.instantiate_bound_regions_with_erased(
+                            instance.args.as_coroutine_closure().coroutine_closure_sig(),
+                        )
+                        .tupled_inputs_ty,
+                    ),
                 ),
                 x => bug!("Unexpected type kind for closure-like: {x:?}"),
             };
-            let trait_ref = ty::TraitRef::new(tcx, trait_id, [closure_ty, inputs]);
+            let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into));
+            let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args);
             let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
             let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
             // There should be exactly one method on this trait, and it should be the one we're
diff --git a/tests/ui/sanitizer/cfi-coroutine.rs b/tests/ui/sanitizer/cfi-coroutine.rs
index 24e59cf5b4d0..5c6a489a7e89 100644
--- a/tests/ui/sanitizer/cfi-coroutine.rs
+++ b/tests/ui/sanitizer/cfi-coroutine.rs
@@ -3,6 +3,7 @@
 //@ revisions: cfi kcfi
 // FIXME(#122848) Remove only-linux once OSX CFI binaries work
 //@ only-linux
+//@ edition: 2024
 //@ [cfi] needs-sanitizer-cfi
 //@ [kcfi] needs-sanitizer-kcfi
 //@ compile-flags: -C target-feature=-crt-static
@@ -10,16 +11,22 @@
 //@ [cfi] compile-flags: -Z sanitizer=cfi
 //@ [kcfi] compile-flags: -Z sanitizer=kcfi
 //@ [kcfi] compile-flags: -C panic=abort -Z panic-abort-tests -C prefer-dynamic=off
-//@ compile-flags: --test
+//@ compile-flags: --test -Z unstable-options
 //@ run-pass
 
 #![feature(coroutines)]
 #![feature(coroutine_trait)]
+#![feature(noop_waker)]
+#![feature(gen_blocks)]
+#![feature(async_iterator)]
 
 use std::ops::{Coroutine, CoroutineState};
 use std::pin::{pin, Pin};
+use std::task::{Context, Poll, Waker};
+use std::async_iter::AsyncIterator;
 
-fn main() {
+#[test]
+fn general_coroutine() {
     let mut coro = |x: i32| {
         yield x;
         "done"
@@ -28,3 +35,33 @@ fn main() {
     assert_eq!(abstract_coro.as_mut().resume(2), CoroutineState::Yielded(2));
     assert_eq!(abstract_coro.as_mut().resume(0), CoroutineState::Complete("done"));
 }
+
+async fn async_fn() {}
+
+#[test]
+fn async_coroutine() {
+    let f: fn() -> Pin>> = || Box::pin(async_fn());
+    let _ = async { f().await; };
+    assert_eq!(f().as_mut().poll(&mut Context::from_waker(Waker::noop())), Poll::Ready(()));
+}
+
+async gen fn async_gen_fn() -> u8 {
+    yield 5;
+}
+
+#[test]
+fn async_gen_coroutine() {
+    let f: fn() -> Pin>> = || Box::pin(async_gen_fn());
+    assert_eq!(f().as_mut().poll_next(&mut Context::from_waker(Waker::noop())),
+               Poll::Ready(Some(5)));
+}
+
+gen fn gen_fn() -> u8 {
+    yield 6;
+}
+
+#[test]
+fn gen_coroutine() {
+    let f: fn() -> Box> = || Box::new(gen_fn());
+    assert_eq!(f().next(), Some(6));
+}

From b626f0131027c0b352274316d34f1f61df78faa3 Mon Sep 17 00:00:00 2001
From: Ana Hobden 
Date: Tue, 2 Apr 2024 10:44:23 -0700
Subject: [PATCH 188/192] Update sysinfo to 0.30.8

Fixes a Mac specific issue when using `build-metrics = true` in `config.toml`
---
 Cargo.lock | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index a10eccb1ce51..516f867dc404 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5368,9 +5368,9 @@ dependencies = [
 
 [[package]]
 name = "sysinfo"
-version = "0.30.7"
+version = "0.30.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18"
+checksum = "4b1a378e48fb3ce3a5cf04359c456c9c98ff689bcf1c1bc6e6a31f247686f275"
 dependencies = [
  "cfg-if",
  "core-foundation-sys",

From a0721036b6fdfac5c239fc2f1f291ab70dd91aff Mon Sep 17 00:00:00 2001
From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com>
Date: Tue, 2 Apr 2024 20:36:48 +0200
Subject: [PATCH 189/192] Improve bootstrap comments

Rewrote a comment I found hard to understand, added some more.
---
 src/bootstrap/src/core/build_steps/compile.rs | 10 ++++------
 src/bootstrap/src/core/build_steps/llvm.rs    |  1 +
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 895c43d4dda6..14eee764ed13 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -825,6 +825,7 @@ fn cp_rustc_component_to_ci_sysroot(
 #[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)]
 pub struct Rustc {
     pub target: TargetSelection,
+    /// The **previous** compiler used to compile this compiler.
     pub compiler: Compiler,
     /// Whether to build a subset of crates, rather than the whole compiler.
     ///
@@ -1518,12 +1519,9 @@ impl Step for Sysroot {
         run.never()
     }
 
-    /// Returns the sysroot for the `compiler` specified that *this build system
-    /// generates*.
-    ///
-    /// That is, the sysroot for the stage0 compiler is not what the compiler
-    /// thinks it is by default, but it's the same as the default for stages
-    /// 1-3.
+    /// Returns the sysroot that `compiler` is supposed to use.
+    /// For the stage0 compiler, this is stage0-sysroot (because of the initial std build).
+    /// For all other stages, it's the same stage directory that the compiler lives in.
     fn run(self, builder: &Builder<'_>) -> PathBuf {
         let compiler = self.compiler;
         let host_dir = builder.out.join(compiler.host.triple);
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 3da927b5fa0f..e461e11677fa 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -274,6 +274,7 @@ impl Step for Llvm {
             target.to_string()
         };
 
+        // If LLVM has already been built or been downloaded through download-ci-llvm, we avoid building it again.
         let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) {
             Ok(p) => return p,
             Err(m) => m,

From 0fcdf3486146b5b2fc5d4b7f3bcaffbbb6a95fdc Mon Sep 17 00:00:00 2001
From: Jacob Pratt 
Date: Fri, 29 Mar 2024 05:35:45 +0000
Subject: [PATCH 190/192] Avoid expanding to unstable internal method

---
 compiler/rustc_expand/src/build.rs            | 42 +++++++++++++++----
 tests/ui/derives/auxiliary/rustc-serialize.rs | 16 +++++++
 .../derives/rustc-decodable-issue-123156.rs   | 11 +++++
 .../rustc-decodable-issue-123156.stderr       | 10 +++++
 4 files changed, 70 insertions(+), 9 deletions(-)
 create mode 100644 tests/ui/derives/auxiliary/rustc-serialize.rs
 create mode 100644 tests/ui/derives/rustc-decodable-issue-123156.rs
 create mode 100644 tests/ui/derives/rustc-decodable-issue-123156.stderr

diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 30559871b4e2..cdcf67b26f80 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -48,6 +48,23 @@ impl<'a> ExtCtxt<'a> {
         ast::Path { span, segments, tokens: None }
     }
 
+    pub fn macro_call(
+        &self,
+        span: Span,
+        path: ast::Path,
+        delim: ast::token::Delimiter,
+        tokens: ast::tokenstream::TokenStream,
+    ) -> P {
+        P(ast::MacCall {
+            path,
+            args: P(ast::DelimArgs {
+                dspan: ast::tokenstream::DelimSpan { open: span, close: span },
+                delim,
+                tokens,
+            }),
+        })
+    }
+
     pub fn ty_mt(&self, ty: P, mutbl: ast::Mutability) -> ast::MutTy {
         ast::MutTy { ty, mutbl }
     }
@@ -265,6 +282,10 @@ impl<'a> ExtCtxt<'a> {
         self.expr(span, ast::ExprKind::Field(expr, field))
     }
 
+    pub fn expr_macro_call(&self, span: Span, call: P) -> P {
+        self.expr(span, ast::ExprKind::MacCall(call))
+    }
+
     pub fn expr_binary(
         &self,
         sp: Span,
@@ -410,16 +431,19 @@ impl<'a> ExtCtxt<'a> {
         self.expr(sp, ast::ExprKind::Tup(exprs))
     }
 
-    pub fn expr_fail(&self, span: Span, msg: Symbol) -> P {
-        self.expr_call_global(
-            span,
-            [sym::std, sym::rt, sym::begin_panic].iter().map(|s| Ident::new(*s, span)).collect(),
-            thin_vec![self.expr_str(span, msg)],
-        )
-    }
-
     pub fn expr_unreachable(&self, span: Span) -> P {
-        self.expr_fail(span, Symbol::intern("internal error: entered unreachable code"))
+        self.expr_macro_call(
+            span,
+            self.macro_call(
+                span,
+                self.path_global(
+                    span,
+                    [sym::std, sym::unreachable].map(|s| Ident::new(s, span)).to_vec(),
+                ),
+                ast::token::Delimiter::Parenthesis,
+                ast::tokenstream::TokenStream::default(),
+            ),
+        )
     }
 
     pub fn expr_ok(&self, sp: Span, expr: P) -> P {
diff --git a/tests/ui/derives/auxiliary/rustc-serialize.rs b/tests/ui/derives/auxiliary/rustc-serialize.rs
new file mode 100644
index 000000000000..24177af931c4
--- /dev/null
+++ b/tests/ui/derives/auxiliary/rustc-serialize.rs
@@ -0,0 +1,16 @@
+#![crate_type = "lib"]
+
+pub trait Decoder {
+    type Error;
+
+    fn read_enum(&mut self, name: &str, f: F) -> Result
+        where F: FnOnce(&mut Self) -> Result;
+    fn read_enum_variant(&mut self, names: &[&str], f: F)
+                               -> Result
+        where F: FnMut(&mut Self, usize) -> Result;
+
+}
+
+pub trait Decodable: Sized {
+    fn decode(d: &mut D) -> Result;
+}
diff --git a/tests/ui/derives/rustc-decodable-issue-123156.rs b/tests/ui/derives/rustc-decodable-issue-123156.rs
new file mode 100644
index 000000000000..1983837ed8d4
--- /dev/null
+++ b/tests/ui/derives/rustc-decodable-issue-123156.rs
@@ -0,0 +1,11 @@
+//@ check-pass
+//@ edition:2021
+//@ aux-build:rustc-serialize.rs
+
+#![crate_type = "lib"]
+#![allow(deprecated, soft_unstable)]
+
+extern crate rustc_serialize;
+
+#[derive(RustcDecodable)]
+pub enum Foo {}
diff --git a/tests/ui/derives/rustc-decodable-issue-123156.stderr b/tests/ui/derives/rustc-decodable-issue-123156.stderr
new file mode 100644
index 000000000000..ee7b33d59bb9
--- /dev/null
+++ b/tests/ui/derives/rustc-decodable-issue-123156.stderr
@@ -0,0 +1,10 @@
+Future incompatibility report: Future breakage diagnostic:
+warning: use of unstable library feature 'rustc_encodable_decodable': derive macro for `rustc-serialize`; should not be used in new code
+  --> $DIR/rustc-decodable-issue-123156.rs:10:10
+   |
+LL | #[derive(RustcDecodable)]
+   |          ^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #64266 
+

From 036085dfec3f58cf63f8014f6c94adddace3b527 Mon Sep 17 00:00:00 2001
From: Justin Karneges 
Date: Tue, 2 Apr 2024 15:45:53 -0700
Subject: [PATCH 191/192] set tracking issue

---
 library/core/src/task/wake.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs
index 98e2c2912ef6..fe39f6c0b569 100644
--- a/library/core/src/task/wake.rs
+++ b/library/core/src/task/wake.rs
@@ -276,7 +276,7 @@ impl<'a> Context<'a> {
 
     /// Returns a reference to the extension data for the current task.
     #[inline]
-    #[unstable(feature = "context_ext", issue = "none")]
+    #[unstable(feature = "context_ext", issue = "123392")]
     #[rustc_const_unstable(feature = "const_waker", issue = "102012")]
     pub const fn ext(&mut self) -> &mut dyn Any {
         match &mut self.ext {
@@ -351,7 +351,7 @@ impl<'a> ContextBuilder<'a> {
     /// Create a ContextBuilder from an existing Context.
     #[inline]
     #[rustc_const_unstable(feature = "const_waker", issue = "102012")]
-    #[unstable(feature = "context_ext", issue = "none")]
+    #[unstable(feature = "context_ext", issue = "123392")]
     pub const fn from(cx: &'a mut Context<'_>) -> Self {
         let ext = match &mut cx.ext {
             ExtData::Some(ext) => ExtData::Some(*ext),
@@ -368,7 +368,7 @@ impl<'a> ContextBuilder<'a> {
 
     /// This method is used to set the value for the waker on `Context`.
     #[inline]
-    #[unstable(feature = "context_ext", issue = "none")]
+    #[unstable(feature = "context_ext", issue = "123392")]
     #[rustc_const_unstable(feature = "const_waker", issue = "102012")]
     pub const fn waker(self, waker: &'a Waker) -> Self {
         Self { waker, ..self }
@@ -384,7 +384,7 @@ impl<'a> ContextBuilder<'a> {
 
     /// This method is used to set the value for the extension data on `Context`.
     #[inline]
-    #[unstable(feature = "context_ext", issue = "none")]
+    #[unstable(feature = "context_ext", issue = "123392")]
     #[rustc_const_unstable(feature = "const_waker", issue = "102012")]
     pub const fn ext(self, data: &'a mut dyn Any) -> Self {
         Self { ext: ExtData::Some(data), ..self }

From 2d47cd77ac7b41a08f5c2ebc22035ed2f39dc076 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Wed, 3 Apr 2024 12:55:40 +1100
Subject: [PATCH 192/192] Check `x86_64` size assertions on `aarch64`, too

This makes it easier for contributors on aarch64 workstations (e.g. Macs) to
notice when these assertions have been violated.
---
 compiler/rustc_ast/src/ast.rs                        | 2 +-
 compiler/rustc_ast/src/token.rs                      | 2 +-
 compiler/rustc_ast/src/tokenstream.rs                | 2 +-
 compiler/rustc_const_eval/src/interpret/operand.rs   | 2 +-
 compiler/rustc_const_eval/src/interpret/place.rs     | 2 +-
 compiler/rustc_errors/src/lib.rs                     | 4 ++--
 compiler/rustc_expand/src/mbe/macro_parser.rs        | 2 +-
 compiler/rustc_hir/src/hir.rs                        | 2 +-
 compiler/rustc_index/src/bit_set.rs                  | 2 +-
 compiler/rustc_infer/src/infer/mod.rs                | 2 +-
 compiler/rustc_infer/src/lib.rs                      | 2 +-
 compiler/rustc_infer/src/traits/mod.rs               | 2 +-
 compiler/rustc_middle/src/mir/consts.rs              | 2 +-
 compiler/rustc_middle/src/mir/interpret/error.rs     | 2 +-
 compiler/rustc_middle/src/mir/interpret/value.rs     | 2 +-
 compiler/rustc_middle/src/mir/mod.rs                 | 2 +-
 compiler/rustc_middle/src/mir/query.rs               | 2 +-
 compiler/rustc_middle/src/mir/syntax.rs              | 2 +-
 compiler/rustc_middle/src/mir/tcx.rs                 | 2 +-
 compiler/rustc_middle/src/query/plumbing.rs          | 4 ++--
 compiler/rustc_middle/src/thir.rs                    | 2 +-
 compiler/rustc_middle/src/traits/mod.rs              | 2 +-
 compiler/rustc_middle/src/ty/consts.rs               | 2 +-
 compiler/rustc_middle/src/ty/consts/kind.rs          | 4 ++--
 compiler/rustc_middle/src/ty/mod.rs                  | 2 +-
 compiler/rustc_middle/src/ty/sty.rs                  | 2 +-
 compiler/rustc_parse/src/lexer/mod.rs                | 2 +-
 compiler/rustc_parse/src/parser/attr_wrapper.rs      | 2 +-
 compiler/rustc_parse/src/parser/mod.rs               | 2 +-
 compiler/rustc_parse_format/src/lib.rs               | 2 +-
 compiler/rustc_target/src/abi/call/mod.rs            | 2 +-
 compiler/rustc_trait_selection/src/lib.rs            | 2 +-
 compiler/rustc_trait_selection/src/traits/fulfill.rs | 2 +-
 33 files changed, 36 insertions(+), 36 deletions(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index aba94f4d817d..4ba298902a93 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -3341,7 +3341,7 @@ impl TryFrom for ForeignItemKind {
 pub type ForeignItem = Item;
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index f49eb2f22c50..5060bbec4216 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -1021,7 +1021,7 @@ where
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index 239735456ad5..f3249f3e5a8b 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -768,7 +768,7 @@ impl DelimSpacing {
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 831787a92c86..842fb6d204c2 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -792,7 +792,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 1a2f1194f89a..e32aea39fc59 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -1058,7 +1058,7 @@ where
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 7b40954e735d..c47abf5e988e 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -102,9 +102,9 @@ pub type PResult<'a, T> = Result>;
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
 // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16);
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16);
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index a31be05ccc4d..9fff00ffeae1 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -266,7 +266,7 @@ struct MatcherPos {
 }
 
 // This type is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(MatcherPos, 16);
 
 impl MatcherPos {
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index a0f86565929b..f6d63a293ba2 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3762,7 +3762,7 @@ impl<'hir> Node<'hir> {
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     // tidy-alphabetical-start
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index 12f8e42c78f9..c7e563035fc3 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -400,7 +400,7 @@ enum Chunk {
 }
 
 // This type is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 crate::static_assert_size!(Chunk, 16);
 
 impl ChunkedBitSet {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 339c8ac10b33..3e89327d20fd 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -483,7 +483,7 @@ pub enum SubregionOrigin<'tcx> {
 }
 
 // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(SubregionOrigin<'_>, 32);
 
 impl<'tcx> SubregionOrigin<'tcx> {
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index ee9ce842d00a..0444cbe2ee41 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -32,7 +32,7 @@
 
 #[macro_use]
 extern crate rustc_macros;
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 #[macro_use]
 extern crate rustc_data_structures;
 #[macro_use]
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs
index 616f5cc04564..94ad0f5b1c86 100644
--- a/compiler/rustc_infer/src/traits/mod.rs
+++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -112,7 +112,7 @@ impl<'tcx> PolyTraitObligation<'tcx> {
 }
 
 // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(PredicateObligation<'_>, 48);
 
 pub type PredicateObligations<'tcx> = Vec>;
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index 2663a6b551ec..155af0620127 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -70,7 +70,7 @@ pub enum ConstValue<'tcx> {
     },
 }
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(ConstValue<'_>, 24);
 
 impl<'tcx> ConstValue<'tcx> {
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index c86970635a55..e9be26d058b4 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -88,7 +88,7 @@ pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>;
 /// This is needed in `thir::pattern::lower_inline_const`.
 pub type EvalToValTreeResult<'tcx> = Result>, ErrorHandled>;
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(InterpErrorInfo<'_>, 8);
 
 /// Packages the kind of error we got from the const code interpreter
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 24d4a79c7d79..9f9433e483bc 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -37,7 +37,7 @@ pub enum Scalar {
     Ptr(Pointer, u8),
 }
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(Scalar, 24);
 
 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index e5a650c5ac4a..e046f076389b 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1881,7 +1881,7 @@ impl DefLocation {
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 731e050ca9b7..0a567e2781dc 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -276,7 +276,7 @@ pub struct ClosureOutlivesRequirement<'tcx> {
 }
 
 // Make sure this enum doesn't unintentionally grow
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16);
 
 /// Outlives-constraints can be categorized to determine whether and why they
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 1f6165babdfb..eae6ef8c396a 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1453,7 +1453,7 @@ pub enum BinOp {
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     // tidy-alphabetical-start
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 74c2aef8fcc2..b86aa601ce8e 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -14,7 +14,7 @@ pub struct PlaceTy<'tcx> {
 }
 
 // At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(PlaceTy<'_>, 16);
 
 impl<'tcx> PlaceTy<'tcx> {
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index e3588a7afdc1..8cb4ee7bd41e 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -339,7 +339,7 @@ macro_rules! define_callbacks {
                 pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>;
 
                 // Ensure that keys grow no larger than 64 bytes
-                #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+                #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))]
                 const _: () = {
                     if mem::size_of::>() > 64 {
                         panic!("{}", concat!(
@@ -353,7 +353,7 @@ macro_rules! define_callbacks {
                 };
 
                 // Ensure that values grow no larger than 64 bytes
-                #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+                #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))]
                 const _: () = {
                     if mem::size_of::>() > 64 {
                         panic!("{}", concat!(
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 05f6fbbbfa33..9bb38a893eb6 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -1204,7 +1204,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     // tidy-alphabetical-start
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index efea2a66bb21..ee8167919197 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -550,7 +550,7 @@ impl<'tcx> ObligationCauseCode<'tcx> {
 }
 
 // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(ObligationCauseCode<'_>, 48);
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 3713883eb00d..49b806b8369f 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -59,7 +59,7 @@ pub struct ConstData<'tcx> {
     pub kind: ConstKind<'tcx>,
 }
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(ConstData<'_>, 40);
 
 impl<'tcx> Const<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index ea02faca5f39..94e41709f5da 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -71,8 +71,8 @@ pub enum Expr<'tcx> {
     Cast(CastKind, Const<'tcx>, Ty<'tcx>),
 }
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(Expr<'_>, 24);
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(super::ConstKind<'_>, 32);
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 4e1baaec39ea..191cdafacb6b 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2168,7 +2168,7 @@ pub struct DestructuredConst<'tcx> {
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index b5e619f1e2a3..fb511f2275fe 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2699,7 +2699,7 @@ impl<'tcx> VarianceDiagInfo<'tcx> {
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 63b2b47630b2..69b48bf0aff7 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -30,7 +30,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char};
 //
 // This assertion is in this crate, rather than in `rustc_lexer`, because that
 // crate cannot depend on `rustc_data_structures`.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12);
 
 #[derive(Clone, Debug)]
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index a1dd7d6f6734..baaed5ec37b7 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -454,7 +454,7 @@ fn make_token_stream(
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 1971591364d3..09bc00403f38 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -179,7 +179,7 @@ pub struct Parser<'a> {
 
 // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
 // it doesn't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(Parser<'_>, 264);
 
 /// Stores span information about a closure.
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 2bb4b09e337c..ccda43c827c5 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -1087,7 +1087,7 @@ fn unescape_string(string: &str) -> Option {
 }
 
 // Assert a reasonable size for `Piece`
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 rustc_index::static_assert_size!(Piece<'_>, 16);
 
 #[cfg(test)]
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index 486afc5f8f30..706a7a310f2b 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -927,7 +927,7 @@ impl FromStr for Conv {
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index e14fc62cd6f0..b5fb710e4cc4 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -30,7 +30,7 @@
 
 #[macro_use]
 extern crate rustc_macros;
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 #[macro_use]
 extern crate rustc_data_structures;
 #[macro_use]
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 34c891d400e4..b5be9a2bcb35 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -72,7 +72,7 @@ pub struct PendingPredicateObligation<'tcx> {
 }
 
 // `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
 static_assert_size!(PendingPredicateObligation<'_>, 72);
 
 impl<'tcx> FulfillmentContext<'tcx> {