From 6cc4b6f5b0baff50056707422ff3286695665243 Mon Sep 17 00:00:00 2001 From: Yara Date: Tue, 15 Sep 2026 16:40:04 +0200 Subject: [PATCH 01/17] reflection: prefix reflection intrinsics with type_id --- .../src/const_eval/machine.rs | 10 ++++---- .../src/const_eval/type_info/adt.rs | 2 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 24 ++++++++++++------- compiler/rustc_span/src/symbol.rs | 11 +++++---- library/core/src/intrinsics/mod.rs | 10 ++++---- library/core/src/mem/type_info.rs | 14 +++++------ 6 files changed, 39 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4251c74eb1ffb..db92b9b3aaceb 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -602,7 +602,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } - sym::type_of => { + sym::type_id_type_of => { let ty = ecx.read_type_id(&args[0])?; ecx.write_type_info(ty, dest)?; } @@ -621,7 +621,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_bool(is_mutable), dest)?; } - sym::size_of_type_id => { + sym::type_id_size_of => { let ty = ecx.read_type_id(&args[0])?; let layout = ecx.layout_of(ty)?; let variant_index = if layout.is_sized() { @@ -758,7 +758,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_target_usize(variants_num as u64, ecx), dest)?; } - sym::variant_name => { + sym::type_id_variant_name => { let base = ecx.read_type_id(&args[0])?; let field_name = if let ty::Adt(def, _) = base.kind() { @@ -784,7 +784,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { )?; } - sym::variant_non_exhaustive => { + sym::type_id_variant_non_exhaustive => { let base = ecx.read_type_id(&args[0])?; let non_exhaustive = if let ty::Adt(def, _) = base.kind() { @@ -884,7 +884,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_type_id_generics(dest, ty)?; } - sym::non_exhaustive => { + sym::type_id_non_exhaustive => { let ty = ecx.read_type_id(&args[0])?; // FIXME(reflection): need a way to obtain non-exhaustiveness of a variant's fields. diff --git a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs index 09662d2bc0a98..bd26e1369996b 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs @@ -127,7 +127,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { match field.name { sym::generics => self.write_generics(&field_place, generics)?, - sym::variants => { + sym::type_id_variants => { self.allocate_fill_and_write_slice_ptr( &field_place, enum_def.variants().len() as u64, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cca93e8aef0ec..88a93229296c2 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -150,7 +150,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::minimumf128 | sym::mul_with_overflow | sym::needs_drop - | sym::non_exhaustive | sym::offload | sym::offload_get_num_devices | sym::offset_of @@ -199,6 +198,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf64 | sym::truncf128 | sym::type_id + | sym::type_id_non_exhaustive | sym::type_id_array_len | sym::type_id_element_ty | sym::type_id_eq @@ -212,12 +212,14 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_id_variants | sym::type_id_vtable | sym::type_name - | sym::type_of + | sym::type_id_type_of + | sym::type_id_size_of + | sym::type_id_variants + | sym::type_id_variant_name + | sym::type_id_variant_non_exhaustive | sym::ub_checks | sym::va_copy | sym::variant_count - | sym::variant_name - | sym::variant_non_exhaustive | sym::wrapping_add | sym::wrapping_mul | sym::wrapping_sub @@ -296,7 +298,7 @@ pub(crate) fn check_intrinsic_type( sym::size_of_val | sym::align_of_val => { (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } - sym::size_of_type_id => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), + sym::type_id_size_of => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), @@ -339,8 +341,12 @@ pub(crate) fn check_intrinsic_type( sym::type_id_points_mutably => (0, 0, vec![type_id_ty()], tcx.types.bool), sym::type_id_points_to => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, type_id_ty())), sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize), - sym::variant_name => (0, 0, vec![type_id_ty(), tcx.types.usize], Ty::new_static_str(tcx)), - sym::variant_non_exhaustive => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.bool), + sym::type_id_variant_name => { + (0, 0, vec![type_id_ty(), tcx.types.usize], Ty::new_static_str(tcx)) + } + sym::type_id_variant_non_exhaustive => { + (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.bool) + } sym::type_id_vtable => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); @@ -355,7 +361,7 @@ pub(crate) fn check_intrinsic_type( (0, 0, vec![type_id_ty(); 2], ret_ty) } - sym::type_of => ( + sym::type_id_type_of => ( 0, 0, vec![type_id_ty()], @@ -364,7 +370,7 @@ pub(crate) fn check_intrinsic_type( sym::field_representing_type_actual_type_id => (0, 0, vec![type_id_ty()], type_id_ty()), sym::field_representing_type_name => (0, 0, vec![type_id_ty()], Ty::new_static_str(tcx)), sym::field_representing_type_offset => (0, 0, vec![type_id_ty()], tcx.types.usize), - sym::non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), + sym::type_id_non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), sym::type_id_generics => ( 0, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4c1b9c78b7963..3adfb62d1ce54 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2203,8 +2203,14 @@ symbols! { type_id_function_ptr, type_id_generics, type_id_is_signed, + type_id_non_exhaustive, type_id_points_mutably, type_id_points_to, + type_id_size_of, + type_name, + type_id_type_of, + type_id_variant_name, + type_id_variant_non_exhaustive, type_id_variants, type_id_vtable, type_info, @@ -2215,8 +2221,6 @@ symbols! { type_ir_interner, type_length_limit, type_macros, - type_name, - type_of, type_privacy_lints, typed_swap_nonoverlapping, u8, @@ -2344,9 +2348,6 @@ symbols! { var, variadic, variant_count, - variant_name, - variant_non_exhaustive, - variants, vec, vector, verbatim, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index ab2bbb8ab4449..12cb3d2b6d8b4 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2979,7 +2979,7 @@ pub fn type_id_vtable( #[rustc_intrinsic] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_comptime] -pub fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type; +pub fn type_id_type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type; /// Gets a static string slice containing the name of a type. /// @@ -3056,7 +3056,7 @@ pub fn type_id_element_ty(_id: crate::any::TypeId) -> Option #[rustc_intrinsic] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_comptime] -pub fn size_of_type_id(_id: crate::any::TypeId) -> Option; +pub fn type_id_size_of(_id: crate::any::TypeId) -> Option; /// Gets the number of variants of the type represented by this `TypeId`. /// @@ -3074,7 +3074,7 @@ pub fn type_id_variants(_id: crate::any::TypeId) -> usize; #[rustc_intrinsic] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_comptime] -pub fn variant_name(_base: crate::any::TypeId, _variant_index: usize) -> &'static str; +pub fn type_id_variant_name(_base: crate::any::TypeId, _variant_index: usize) -> &'static str; /// Returns true when the variant represented by the base `TypeId` and variant_idx is non /// exhaustive. @@ -3086,7 +3086,7 @@ pub fn variant_name(_base: crate::any::TypeId, _variant_index: usize) -> &'stati #[rustc_intrinsic] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_comptime] -pub fn variant_non_exhaustive(base: crate::any::TypeId, variant: usize) -> bool; +pub fn type_id_variant_non_exhaustive(base: crate::any::TypeId, variant: usize) -> bool; /// Gets the number of fields at the given `variant_index` represented by this `TypeId`. /// @@ -3155,7 +3155,7 @@ pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option bool; +pub fn type_id_non_exhaustive(_id: crate::any::TypeId) -> bool; /// Returns the list of generic args on this type. /// Only meaningful for Adts, closures, ... Everything else returns an empty slice. diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index a11d1f1d0dc18..1e96cf915e67d 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -3,7 +3,7 @@ use crate::any::TypeId; use crate::fmt; -use crate::intrinsics::{self, type_id, type_of}; +use crate::intrinsics; use crate::marker::PointeeSized; use crate::ptr::DynMetadata; @@ -38,7 +38,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn info(self) -> Type { - type_of(self) + intrinsics::type_id_type_of(self) } } @@ -54,7 +54,7 @@ impl Type { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] pub const fn of() -> Self { - const { type_id::().info() } + const { intrinsics::type_id::().info() } } } @@ -328,7 +328,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn size(self) -> Option { - intrinsics::size_of_type_id(self) + intrinsics::type_id_size_of(self) } /// Returns the number of variants of the type represented by this `TypeId`. @@ -566,7 +566,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn non_exhaustive(self) -> bool { - intrinsics::non_exhaustive(self) + intrinsics::type_id_non_exhaustive(self) } /// Returns a list of generic parameters of the type. @@ -696,7 +696,7 @@ impl VariantId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn name(self) -> &'static str { - intrinsics::variant_name(self.base, self.variant) + intrinsics::type_id_variant_name(self.base, self.variant) } /// Returns whether this variant is marked with `#[non_exhaustive]`. @@ -704,7 +704,7 @@ impl VariantId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn non_exhaustive(self) -> bool { - intrinsics::variant_non_exhaustive(self.base, self.variant) + intrinsics::type_id_variant_non_exhaustive(self.base, self.variant) } } From 13035c1c803e33a62e1c8bea4a7ce0569f420cb4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 30 Sep 2026 12:10:04 +0200 Subject: [PATCH 02/17] Iterate only once to get module inner attributes position --- compiler/rustc_resolve/src/late.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index ab595de3e39e7..3c7304e1dc92d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2861,12 +2861,14 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } fn resolve_item(&mut self, item: &'ast Item) { + let mut mod_inner_attrs_start = None; match item.kind { ItemKind::Mod(..) => { // We only handle outer doc comments for modules here. let attrs = if let Some(pos) = item.attrs.iter().position(|a| { a.doc_resolution_scope().is_some_and(|style| style == AttrStyle::Inner) }) { + mod_inner_attrs_start = Some(pos); &item.attrs[..pos] } else { &item.attrs @@ -2972,9 +2974,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { this.with_rib(TypeNS, RibKind::Module(module.expect_local()), |this| { // Outer doc comments were already handled above, now we handle // inner doc comments. - let attrs = if let Some(pos) = item.attrs.iter().position(|a| { - a.doc_resolution_scope().is_some_and(|style| style == AttrStyle::Inner) - }) { + let attrs = if let Some(pos) = mod_inner_attrs_start { &item.attrs[pos..] } else { &[] From 9d32c0049a4672bb3ab824ec8fb5ba89ac7af7b0 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 30 Sep 2026 20:45:59 -0700 Subject: [PATCH 03/17] bootstrap: Enable rustdoc mergeable CCI for std and internal docs Takes a different approach to https://github.com/rust-lang/rust/pull/160098, where the internal docs are merged by bootstrap directly invoking rustdoc. This requires bootstrap to gather the list of metadata directories by inspecting cargo's fingerprint files (which aren't stable). The first commit is written by @camelid, but I wrote the other two. This feature is needed because: 1. The search index (that powers [web-based search](https://doc.rust-lang.org/nightly/nightly-rustc/?search=ty%20-%3E%20rustdoc%3A%3Atype)) needs to contain all of the crates in the nightly-rustc project. In particular, I'd prefer if it contained Clippy, Rustdoc, and Rustc, since those crates share type checker stuff and the ability to search all three at once is convenient. 2. For every crate that rustdoc *currently* documents, it has to load the search index from the doc output dir, and rebuild the search index with the new crate added to it. Loading the search index requires $O(\text{crates})$ work, so doing it once for every crate means we're doing $O(\text{crates}^2)$ work overall. 3. It would be more efficient, *instead*, if each crate wrote its data separately, and then the final search index was generated at the end by merging them all at once. Obviously, this would make the work linear instead of quadratic. For the record, Hoogle and Sherlodoc have a similar index-generating step. 4. We call this "Mergeable Cross-Crate-Information." Cargo stores it in the build directory, and supplies it to Rustdoc in a separate phase that runs after everything else. When we eventually stabilize this feature, it will be invisible to (most) end users. `cargo doc` will just be faster. 5. So, in order for crates to share their cross-crate info, we need them to share a build directory. 6. Tools, like Rustdoc and Cargo, don't normally share a build directory with Rustc. 7. To make them share a build directory while generating documentation, without forcing them to share a build directory while compiling, I added a new mode. --- This rustdoc feature is unstable but will be stabilized soon, and this is a good way of dogfooding it to make sure it works properly. It should have no effect on the generated docs, but it provides a significant speedup. For example, I measure a 3x speedup locally (3m 11s -> 1m 1s) for `x doc src/tools` -- note that this is with the latest rustdoc perf improvements (rust-lang/rust#159854). --- This reverts commit c84edb3d8cbd74af5df2cb06dbcb7c29d9743ff7. --- src/bootstrap/src/core/build_steps/compile.rs | 10 +- src/bootstrap/src/core/build_steps/dist.rs | 9 +- src/bootstrap/src/core/build_steps/doc.rs | 467 ++++++++++++++---- src/bootstrap/src/core/builder/cargo.rs | 6 + src/bootstrap/src/core/builder/mod.rs | 1 + src/bootstrap/src/core/builder/tests.rs | 114 ++--- src/bootstrap/src/core/session.rs | 2 +- 7 files changed, 417 insertions(+), 192 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 97e7280da1e37..816126b5405b8 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2675,7 +2675,7 @@ pub fn run_cargo( let (filenames_vec, crate_types) = match msg { CargoMessage::CompilerArtifact { filenames, - target: CargoTarget { crate_types }, + target: CargoTarget { crate_types, .. }, .. } => { let mut f: Vec = filenames.into_iter().map(|s| s.into_owned()).collect(); @@ -2882,12 +2882,14 @@ pub fn stream_cargo( status.success() } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct CargoTarget<'a> { - crate_types: Vec>, + pub crate_types: Vec>, + #[serde(default)] + pub doc: bool, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] #[serde(tag = "reason", rename_all = "kebab-case")] pub enum CargoMessage<'a> { CompilerArtifact { filenames: Vec>, target: CargoTarget<'a> }, diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 7073b334c414d..d2cd8c77bc9db 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -23,7 +23,7 @@ use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::compile::{ get_codegen_backend_file, libgccjit_path_relative_to_cg_dir, normalize_codegen_backend_name, }; -use crate::core::build_steps::doc::DocumentationFormat; +use crate::core::build_steps::doc::{CompilerWithTools, DocumentationFormat}; use crate::core::build_steps::gcc::GccTargetPair; use crate::core::build_steps::llvm::{ LLVM_CI_LINK_TYPE_PATH, LlvmBuildStatus, LlvmKind, get_llvm_build_status, @@ -185,7 +185,7 @@ impl CommandLineStep for JsonDocs { } } -/// Builds the `rustc-docs` installer component. +/// Builds the `rustc-docs` component. /// Apart from the documentation of the `rustc_*` crates, it also includes the documentation of /// various in-tree helper tools (bootstrap, build_helper, tidy), /// and also rustc_private tools like rustdoc, clippy, miri or rustfmt. @@ -214,11 +214,12 @@ impl CommandLineStep for RustcDocs { fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.target; - builder.run_default_doc_steps(); + let combined_docs = + builder.ensure(CompilerWithTools::for_stage(builder, builder.top_stage, self.target)); let mut tarball = Tarball::new(builder, "rustc-docs", &target.triple); tarball.set_product_name("Rustc Documentation"); - tarball.add_bulk_dir(builder.compiler_doc_out(target), "share/doc/rust/html/rustc-docs"); + tarball.add_bulk_dir(combined_docs, "share/doc/rust/html/rustc-docs"); tarball.generate() } } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 2c64d7915c392..34e9b354bddb2 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -7,11 +7,13 @@ //! Everything here is basically just a shim around calling either `rustbook` or //! `rustdoc`. +use std::collections::HashSet; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::{env, fs, mem}; use crate::core::build_steps::compile; +use crate::core::build_steps::compile::{CargoMessage, stream_cargo}; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, Tool, prepare_tool_cargo, }; @@ -20,9 +22,9 @@ use crate::core::builder::{ crate_description, }; use crate::core::compiler::Compiler; -use crate::core::config::{Config, TargetSelection}; +use crate::core::config::TargetSelection; use crate::core::session::{FileType, Mode}; -use crate::utils::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; +use crate::utils::helpers::{exit_process, submodule_path_of, symlink_dir, t, up_to_date}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { @@ -840,7 +842,6 @@ fn doc_std( .arg("--no-deps") .arg("--target-dir") .arg(&*target_dir.to_string_lossy()) - .arg("-Zskip-rustdoc-fingerprint") .arg("-Zrustdoc-map") .rustdocflag("--extern-html-root-url") .rustdocflag("std_detect=https://docs.rs/std_detect/latest/") @@ -871,7 +872,159 @@ pub fn prepare_doc_compiler( build_compiler } +/// Run rustdoc to merge cross-crate info metadata (like the search index) from individual +/// executions of rustdoc into `out_dir`. +/// The `json_files` parameter should contain paths to JSON file artifacts generated by previous +/// executions of `cargo doc`. +fn merge_rustdoc_cci( + builder: &Builder<'_>, + build_compiler: Compiler, + json_files: &[PathBuf], + out_dir: &Path, +) { + let mut cmd = builder.rustdoc_cmd(build_compiler); + + cmd.arg("--enable-index-page").arg("-Zunstable-options").arg("-o").arg(out_dir); + + if !builder.config.docs_minification { + cmd.arg("--disable-minification"); + } + + for json_file in json_files { + cmd.arg("--read-doc-meta-dir").arg(json_file.parent().unwrap()); + } + + cmd.run(builder); +} + +/// Generate the combined compiler + tools docs for a given toolchain. +/// This contains both the compiler docs, docs of rustc_private tools (miri, clippy, etc.), cargo +/// and also some bootstrap related tools (bootstrap itself, compiletest, tidy, etc.). +/// +/// It gets hosted at https://doc.rust-lang.org/nightly/nightly-rustc/index.html. +/// +/// Compiler documentation is distributed separately, so we make sure +/// we do not merge it with the other documentation from std, test and +/// proc_macros. This is largely just a wrapper around `cargo doc`. +/// +/// Returns a path to a directory with the generated documentation. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct CompilerWithTools { + build_compiler: Compiler, + target: TargetSelection, + stage: u32, +} + +impl CompilerWithTools { + /// Document `stage` compiler for the given `target`. + pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self { + let build_compiler = prepare_doc_compiler(builder, target, stage); + Self { build_compiler, target, stage } + } +} + +impl CommandLineStep for CompilerWithTools { + type Output = PathBuf; + const IS_HOST: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("compiler-with-tools") + } + + fn is_default_step(builder: &Builder<'_>) -> bool { + builder.config.compiler_docs + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(CompilerWithTools::for_stage( + run.builder, + run.builder.top_stage, + run.target, + )); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let CompilerWithTools { target, build_compiler, stage } = self; + + // This is the intended out directory for combined compiler documentation. + let out = builder.compiler_doc_out(target); + let _ = fs::remove_dir_all(&out); + + let _guard = + builder.msg(Kind::Doc, "compiler-with-tools", Mode::Rustc, build_compiler, target); + + let combined_docs = vec![ + builder.ensure(Rustc::for_stage(builder, stage, target)), + builder.ensure(Rustdoc::new(builder, target)), + builder.ensure(Rustfmt::new(builder, target)), + builder.ensure(Clippy::new(builder, target)), + builder.ensure(Miri::new(builder, target)), + builder.ensure(Cargo::new(builder, target)), + builder.ensure(Tidy::new(builder, target)), + builder.ensure(Bootstrap::new(builder, target)), + builder.ensure(BuildHelper::new(builder, target)), + builder.ensure(Compiletest::new(builder, target)), + builder.ensure(RunMakeSupport::new(builder, target)), + ]; + + if !builder.config.dry_run() { + // Now copy all the individual docs into a single directory + let mut json_files = vec![]; + for docs in combined_docs { + json_files.extend(docs.artifacts.json_files); + + // Doc directories to link to the shared output directory + // We add the host doc dirs, which should already be symlinked in the target + // docs dir at this point (see `merge_host_and_target_docs`). + let dirs_to_copy: Vec<_> = docs + .artifacts + .target_dirs + .iter() + .chain(docs.artifacts.host_dirs.iter()) + .map(|d| d.file_name().unwrap().to_str().unwrap()) + .collect(); + for dir in dirs_to_copy { + // Link the docs dir + let docs_dir = docs.out_dir.join(dir); + assert!(docs_dir.exists(), "Docs directory {docs_dir:?} does not exist."); + let out_docs_dir = out.join(dir); + builder.create_dir(&out_docs_dir); + builder.cp_link_r(&docs_dir, &out_docs_dir); + + // And the src dir + let src_dir = docs.out_dir.join("src").join(dir); + assert!(src_dir.exists(), "Docs source directory {src_dir:?} does not exist."); + let out_src_dir = out.join("src").join(dir); + builder.create_dir(&out_src_dir); + builder.cp_link_r(&src_dir, &out_src_dir); + } + } + // And finally merge all the CCI metadata + merge_rustdoc_cci(builder, build_compiler, &json_files, &out); + } + + // Handle `--open`. + builder.open_in_browser(out.join("index.html")); + out + } + + fn metadata(&self) -> Option { + Some(StepMetadata::doc("CompilerWithTools", self.target).built_by(self.build_compiler)) + } +} + +/// Output of a Doc step. +#[derive(Clone)] +pub struct BuiltDocs { + /// Target doc directory with the generated documentation. + out_dir: PathBuf, + /// Doc artifacts gathered from Cargo during the doc build. + artifacts: DocArtifacts, +} + /// Document the compiler for the given `target` using rustdoc from `build_compiler`. +/// +/// Return the path to the generated rustc documentation directory. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustc { build_compiler: Compiler, @@ -901,7 +1054,7 @@ impl Rustc { } impl CommandLineStep for Rustc { - type Output = (); + type Output = BuiltDocs; const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -922,13 +1075,9 @@ impl CommandLineStep for Rustc { /// Compiler documentation is distributed separately, so we make sure /// we do not merge it with the other documentation from std, test and /// proc_macros. This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.target; - // This is the intended out directory for compiler documentation. - let out = builder.compiler_doc_out(target); - t!(fs::create_dir_all(&out)); - // Build the standard library, so that proc-macros can use it. // (Normally, only the metadata would be necessary, but proc-macros are special since they run at compile-time.) let build_compiler = self.build_compiler; @@ -965,7 +1114,6 @@ impl CommandLineStep for Rustc { cargo.rustdocflag("--generate-macro-expansion"); compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); - cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. // Do link to dependencies on `docs.rs` however using `rustdoc-map`. @@ -977,53 +1125,42 @@ impl CommandLineStep for Rustc { cargo.rustdocflag("--extern-html-root-url"); cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); - let mut to_open = None; - - let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc"); + let cargo_target_dir = builder.stage_out(build_compiler, Mode::Rustc); + let target_doc_dir = cargo_target_dir.join(target).join("doc"); + let host_doc_dir = cargo_target_dir.join("doc"); for krate in &*self.crates { // Create all crate output directories first to make sure rustdoc uses // relative links. // FIXME: Cargo should probably do this itself. - let dir_name = krate.replace('-', "_"); - t!(fs::create_dir_all(out_dir.join(&*dir_name))); + let dir_name = normalize_doc_crate_name(krate); + t!(fs::create_dir_all(target_doc_dir.join(&*dir_name))); cargo.arg("-p").arg(krate); - if to_open.is_none() { - to_open = Some(dir_name); - } } - // This uses a shared directory so that librustdoc documentation gets - // correctly built and merged with the rustc documentation. - // - // This is needed because rustdoc is built in a different directory from - // rustc. rustdoc needs to be able to see everything, for example when - // merging the search index, or generating local (relative) links. - symlink_dir_force(&builder.config, &out, &out_dir); - // Cargo puts proc macros in `target/doc` even if you pass `--target` - // explicitly (https://github.com/rust-lang/cargo/issues/7677). - let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc"); - symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); - - cargo.into_cmd().run(builder); + let artifacts = create_docs_and_gather_artifacts(builder, cargo); + artifacts.sanity_check_crates(builder, self.crates.iter()); if !builder.config.dry_run() { - // Sanity check on linked compiler crates - for krate in &*self.crates { - let dir_name = krate.replace('-', "_"); - // Making sure the directory exists and is not empty. - assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some()); - } + merge_host_and_target_docs(builder, &artifacts, &host_doc_dir, &target_doc_dir); + merge_rustdoc_cci(builder, build_compiler, &artifacts.json_files, &target_doc_dir); } - if builder.paths.iter().any(|path| path.ends_with("compiler")) { - // For `x.py doc compiler --open`, open `rustc_middle` by default. - let index = out.join("rustc_middle").join("index.html"); - builder.open_in_browser(index); - } else if let Some(krate) = to_open { - // Let's open the first crate documentation page: - let index = out.join(krate).join("index.html"); + // We open rustc_middle as the default if invoked as `x.py doc --open RELEASES.md` + // with no particular explicit doc requested (e.g. library/core). + if builder.was_invoked_explicitly::(Kind::Doc) { + let index = if builder.paths.iter().any(|path| path.ends_with("compiler")) { + // For `x.py doc compiler --open`, open `rustc_middle` by default. + target_doc_dir.join("rustc_middle").join("index.html") + } else if let Some(krate) = self.crates.first() { + // Let's open the first crate documentation page: + target_doc_dir.join(normalize_doc_crate_name(krate)).join("index.html") + } else { + target_doc_dir.clone() + }; builder.open_in_browser(index); } + + BuiltDocs { out_dir: target_doc_dir, artifacts } } fn metadata(&self) -> Option { @@ -1031,6 +1168,145 @@ impl CommandLineStep for Rustc { } } +/// Stores generated documentation artifacts. +#[derive(Clone, Debug)] +struct DocArtifacts { + /// Directories with HTML docs for host (proc-macro) crates. + host_dirs: Vec, + /// Directories with HTML docs for target crates. + target_dirs: Vec, + /// JSON files used to create the final CCI index + json_files: Vec, +} + +impl DocArtifacts { + /// Ensure that all passed crates were documented. + fn sanity_check_crates(&self, builder: &Builder<'_>, crates: impl Iterator) + where + S: AsRef, + { + if builder.config.dry_run() { + return; + } + let crate_names: HashSet<&str> = self + .host_dirs + .iter() + .chain(self.target_dirs.iter()) + .filter_map(|d| d.file_name().and_then(|d| d.to_str())) + .collect(); + for krate in crates { + let krate = krate.as_ref(); + let krate = normalize_doc_crate_name(krate); + if !crate_names.contains(krate.as_str()) { + eprintln!("ERROR: crate {krate} was not documented!"); + exit_process(1); + } + } + } +} + +/// Run `cargo doc` and gather generated documentation artifacts. +fn create_docs_and_gather_artifacts(builder: &Builder<'_>, cargo: builder::Cargo) -> DocArtifacts { + let mut json_files = vec![]; + let mut host_dirs = vec![]; + let mut target_dirs = vec![]; + stream_cargo(builder, cargo, vec![], &mut |msg| { + let CargoMessage::CompilerArtifact { filenames, target } = msg else { + return; + }; + if !target.doc { + return; + } + // Note: An alternative way to check host docs would be to check whether the generated + // output is a child of the host doc directory (which we would have to pass to this + // function). + let is_host = target.crate_types.iter().any(|t| t == "proc-macro"); + for filename in filenames { + let path = Path::new(filename.as_ref()); + let Some(extension) = path.extension().and_then(|ext| ext.to_str()) else { + continue; + }; + let path = path.to_path_buf(); + if extension == "json" { + json_files.push(path.to_path_buf()); + } else if extension == "html" { + if is_host { + // doc//index.html -> doc/ + host_dirs.push(path.parent().unwrap().to_path_buf()); + } else { + target_dirs.push(path.parent().unwrap().to_path_buf()); + } + } + } + }); + DocArtifacts { host_dirs, target_dirs, json_files } +} + +/// Merge host and target documentation for a set of crates. +/// We pass `--target` when documenting, so Cargo will put the built documentation into two places: +/// - `target/doc` - contains documentation of host code, so proc macros +/// - `target//doc` - contains documentation of "normal" code +/// +/// See https://github.com/rust-lang/cargo/issues/7677. +/// +/// To produce a single unified documentation, we want to merge them together. +/// We do that by creating symlinks into the target doc dir that will point to the host doc +/// directories. +/// The target doc directory will then contain the combined docs. +fn merge_host_and_target_docs( + builder: &Builder<'_>, + docs: &DocArtifacts, + host_doc_dir: &Path, + target_doc_dir: &Path, +) { + // Sanity check that there is no host/target overlap + for dir in &docs.host_dirs { + let name = dir.file_name().and_then(|d| d.to_str()).unwrap(); + if let Some(target_dir) = docs.target_dirs.iter().find_map(|d| { + let dirname = d.file_name().and_then(|d| d.to_str())?; + if dirname == name { Some(d) } else { None } + }) { + eprintln!( + "ERROR: host docs directory `{name}` ({dir:?}) is also contained in target doc directory ({target_dir:?})" + ); + exit_process(1); + } + } + + let target_src_dir = target_doc_dir.join("src"); + let host_src_dir = host_doc_dir.join("src"); + + // Ideally, we would remove all previous symlinks here. + // However, some of the tools actually share the same build docs directory, so we shouldn't do + // that, otherwise they will invalidate one another. + + for host_docs_crate in &docs.host_dirs { + let dir_name = host_docs_crate.file_name().unwrap().to_str().unwrap(); + // Normalize crate name + let dir_name = normalize_doc_crate_name(dir_name); + + t!(symlink_dir(&builder.config, host_docs_crate, &target_doc_dir.join(&dir_name))); + + // Also symlink its source directory + let target_src_out = target_src_dir.join(&dir_name); + let host_src_out = host_src_dir.join(&dir_name); + t!(symlink_dir(&builder.config, &host_src_out, &target_src_out)); + } + + // Sanity check that all directories contain some documentation + for dir in docs.target_dirs.iter().chain(docs.host_dirs.iter()) { + // Making sure the directory exists and is not empty. + assert!(dir.exists(), "Doc directory {dir:?} does not exist"); + assert!(t!(dir.read_dir()).next().is_some(), "Doc directory {dir:?} is empty"); + } +} + +/// Normalizes crate name to get a name that is used to generate documentation on disk. +/// Turns `rustc-main` into `rustc_main`. +fn normalize_doc_crate_name(name: &str) -> String { + name.replace("-", "_") +} + macro_rules! tool_doc { ( $tool: ident, @@ -1048,46 +1324,53 @@ macro_rules! tool_doc { target: TargetSelection, } - impl CommandLineStep for $tool { - type Output = (); - const IS_HOST: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path($path) - } - - fn is_default_step(builder: &Builder<'_>) -> bool { - builder.config.compiler_docs - } - - fn make_run(run: RunConfig<'_>) { - let target = run.target; + impl $tool { + fn new(builder: &Builder<'_>, target: TargetSelection) -> $tool { let build_compiler = match $mode { Mode::ToolRustcPrivate => { // Rustdoc needs the rustc sysroot available to build. - let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target); + let compilers = RustcPrivateCompilers::new(builder, builder.top_stage, target); // Build rustc docs so that we generate relative links. - run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target)); + builder.ensure(Rustc::from_build_compiler(builder, compilers.build_compiler(), target)); compilers.build_compiler() } Mode::ToolTarget => { // when shipping multiple docs together in one folder, // they all need to use the same rustdoc version - prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage) + prepare_doc_compiler(builder, builder.host_target, builder.top_stage) } _ => { panic!("Unexpected tool mode for documenting: {:?}", $mode); } }; + $tool { build_compiler, mode: $mode, target } + } + fn crates() -> &'static [&'static str] { + &$($crates)?[..] + } + } + + impl CommandLineStep for $tool { + type Output = BuiltDocs; + const IS_HOST: bool = true; - run.builder.ensure($tool { build_compiler, mode: $mode, target }); + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path($path) + } + + fn is_default_step(builder: &Builder<'_>) -> bool { + builder.config.compiler_docs + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure($tool::new(run.builder, run.target)); } /// Generates documentation for a tool. /// /// This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) -> Self::Output { let mut source_type = SourceType::InTree; if let Some(submodule_path) = submodule_path_of(&builder, $path) { @@ -1097,10 +1380,6 @@ macro_rules! tool_doc { let $tool { build_compiler, mode, target } = self; - // This is the intended out directory for compiler documentation. - let out = builder.compiler_doc_out(target); - t!(fs::create_dir_all(&out)); - // Build cargo command. let mut cargo = prepare_tool_cargo( builder, @@ -1122,7 +1401,6 @@ macro_rules! tool_doc { cargo.allow_features(allow_features); } - cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. cargo.arg("--no-deps"); @@ -1130,9 +1408,9 @@ macro_rules! tool_doc { cargo.arg("--lib"); } - $(for krate in $crates { + for krate in $tool::crates() { cargo.arg("-p").arg(krate); - })? + } cargo.rustdocflag("--document-private-items"); // Since we always pass --document-private-items, there's no need to warn about linking to private items. @@ -1141,28 +1419,23 @@ macro_rules! tool_doc { cargo.rustdocflag("--show-type-layout"); cargo.rustdocflag("--generate-link-to-definition"); - let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc"); - $(for krate in $crates { - let dir_name = krate.replace("-", "_"); - t!(fs::create_dir_all(out_dir.join(&*dir_name))); - })? - - // Symlink compiler docs to the output directory of rustdoc documentation. - symlink_dir_force(&builder.config, &out, &out_dir); - let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc"); - symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); + let cargo_target_dir = builder.stage_out(build_compiler, mode); + let target_doc_dir = cargo_target_dir.join(target).join("doc"); + let host_doc_dir = cargo_target_dir.join("doc"); + for krate in $tool::crates() { + let dir_name = normalize_doc_crate_name(krate); + t!(fs::create_dir_all(target_doc_dir.join(&*dir_name))); + } let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target); - cargo.into_cmd().run(builder); + let artifacts = create_docs_and_gather_artifacts(builder, cargo); + artifacts.sanity_check_crates(builder, $tool::crates().iter()); if !builder.config.dry_run() { - // Sanity check on linked doc directories - $(for krate in $crates { - let dir_name = krate.replace("-", "_"); - // Making sure the directory exists and is not empty. - assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some()); - })? + merge_host_and_target_docs(builder, &artifacts, &host_doc_dir, &target_doc_dir); + merge_rustdoc_cci(builder, build_compiler, &artifacts.json_files, &target_doc_dir); } + BuiltDocs { out_dir: target_doc_dir, artifacts } } fn metadata(&self) -> Option { @@ -1344,26 +1617,6 @@ impl CommandLineStep for UnstableBookGen { } } -fn symlink_dir_force(config: &Config, original: &Path, link: &Path) { - if config.dry_run() { - return; - } - if let Ok(m) = fs::symlink_metadata(link) { - if m.file_type().is_dir() { - t!(fs::remove_dir_all(link)); - } else { - // handle directory junctions on windows by falling back to - // `remove_dir`. - t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link))); - } - } - - t!( - symlink_dir(config, original, link), - format!("failed to create link from {} -> {}", link.display(), original.display()) - ); -} - /// Builds the Rust compiler book. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustcBook { diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 754f4a547bd74..98cfd58198a6c 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -723,6 +723,12 @@ impl Builder<'_> { } if cmd_kind == Kind::Doc { + // Will be stabilized soon -> let's dogfood it. + // No effect on doc output but massive doc-generation time improvements. + cargo.arg("-Zrustdoc-mergeable-info"); + + // FIXME: remove this directory clearing here, and do it explicitly in individua doc + // steps, to reduce dependency on implicit doc output paths. let my_out = match mode { // This is the intended out directory for compiler documentation. Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap | Mode::ToolTarget => { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index c221a976b2698..f8b4ebb5841c7 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -965,6 +965,7 @@ impl<'a> Builder<'a> { doc::CargoBook, doc::Clippy, doc::ClippyBook, + doc::CompilerWithTools, doc::Miri, doc::EmbeddedBook, doc::EditionGuide, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index dd1beb65e7fe0..893fc4f7ee017 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -989,58 +989,6 @@ mod snapshot { ); } - #[test] - fn dist_compiler_docs() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("dist") - .path("rustc-docs") - .args(&["--set", "build.compiler-docs=true"]) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 0 -> UnstableBookGen 1 - [build] rustc 0 -> Rustbook 1 - [doc] unstable-book (book) - [doc] book (book) - [doc] book/first-edition (book) - [doc] book/second-edition (book) - [doc] book/2018-edition (book) - [build] rustdoc 1 - [doc] rustc 1 -> standalone 2 - [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] - [doc] rustc 1 -> rustc 2 - [build] rustc 1 -> rustc 2 - [doc] rustc 1 -> Rustdoc 2 - [doc] rustc 1 -> Rustfmt 2 - [build] rustc 1 -> error-index 2 - [doc] rustc 1 -> error-index 2 - [doc] nomicon (book) - [doc] rustc 1 -> reference (book) 2 - [doc] rustdoc (book) - [doc] rust-by-example (book) - [build] rustc 0 -> LintDocs 1 - [doc] rustc (book) - [doc] rustc 1 -> Cargo 2 - [doc] cargo (book) - [doc] rustc 1 -> Clippy 2 - [doc] clippy (book) - [doc] rustc 1 -> Miri 2 - [doc] embedded-book (book) - [doc] edition-guide (book) - [doc] style-guide (book) - [doc] rustc 1 -> Tidy 2 - [doc] rustc 1 -> Bootstrap 2 - [doc] rustc 1 -> releases 2 - [doc] rustc 1 -> RunMakeSupport 2 - [doc] rustc 1 -> BuildHelper 2 - [doc] rustc 1 -> Compiletest 2 - [build] rustc 0 -> RustInstaller 1 - " - ); - } - #[test] fn dist_extended() { let ctx = TestCtx::new(); @@ -1624,35 +1572,24 @@ mod snapshot { ctx .config("dist") .path("rustc-docs") - .render_steps(), @r" + .render_steps(), @" [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [build] rustc 0 -> UnstableBookGen 1 - [build] rustc 0 -> Rustbook 1 - [doc] unstable-book (book) - [doc] book (book) - [doc] book/first-edition (book) - [doc] book/second-edition (book) - [doc] book/2018-edition (book) [build] rustdoc 1 - [doc] rustc 1 -> standalone 2 - [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 1 -> rustc 2 [build] rustc 1 -> rustc 2 - [build] rustc 1 -> error-index 2 - [doc] rustc 1 -> error-index 2 - [doc] nomicon (book) - [doc] rustc 1 -> reference (book) 2 - [doc] rustdoc (book) - [doc] rust-by-example (book) - [build] rustc 0 -> LintDocs 1 - [doc] rustc (book) - [doc] cargo (book) - [doc] clippy (book) - [doc] embedded-book (book) - [doc] edition-guide (book) - [doc] style-guide (book) - [doc] rustc 1 -> releases 2 + [doc] rustc 1 -> Rustdoc 2 + [doc] rustc 1 -> Rustfmt 2 + [doc] rustc 1 -> Clippy 2 + [doc] rustc 1 -> Miri 2 + [doc] rustc 1 -> Cargo 2 + [doc] rustc 1 -> Tidy 2 + [doc] rustc 1 -> Bootstrap 2 + [doc] rustc 1 -> BuildHelper 2 + [doc] rustc 1 -> Compiletest 2 + [doc] rustc 1 -> RunMakeSupport 2 + [doc] rustc 1 -> CompilerWithTools 2 [build] rustc 0 -> RustInstaller 1 "); } @@ -2442,6 +2379,31 @@ mod snapshot { "); } + #[test] + fn doc_compiler_with_tools() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .arg("compiler-with-tools") + .render_steps(), @" + [build] rustdoc 0 + [doc] rustc 0 -> rustc 1 + [build] llvm + [build] rustc 0 -> rustc 1 + [doc] rustc 0 -> Rustdoc 1 + [doc] rustc 0 -> Rustfmt 1 + [doc] rustc 0 -> Clippy 1 + [doc] rustc 0 -> Miri 1 + [doc] rustc 0 -> Cargo 1 + [doc] rustc 0 -> Tidy 1 + [doc] rustc 0 -> Bootstrap 1 + [doc] rustc 0 -> BuildHelper 1 + [doc] rustc 0 -> Compiletest 1 + [doc] rustc 0 -> RunMakeSupport 1 + [doc] rustc 0 -> CompilerWithTools 1 + "); + } + #[test] fn doc_cargo_stage_1() { let ctx = TestCtx::new(); diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index 4ad04df753d00..1808f1b8855bc 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -810,7 +810,7 @@ impl Session { self.out.join(target).join("json-doc") } - /// Output directory for all documentation for a target + /// Output directory for combined compiler + tools docs. pub(crate) fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("compiler-doc") } From 31b874075c5fdf252722ec4b952a9ea1d6ba6d62 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 30 Sep 2026 15:55:59 +0200 Subject: [PATCH 04/17] Miri can do dirfd now --- library/std/src/sys/fs/unix.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index cc40390fa6200..6cbf442c658b1 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1,7 +1,5 @@ #![allow(nonstandard_style)] #![allow(unsafe_op_in_unsafe_fn)] -// miri has some special hacks here that make things unused. -#![cfg_attr(miri, allow(unused))] #[cfg(test)] mod tests; @@ -988,7 +986,6 @@ impl Drop for DirStream { fn drop(&mut self) { // dirfd isn't supported everywhere #[cfg(not(any( - miri, target_os = "redox", target_os = "nto", target_os = "qnx", @@ -1030,16 +1027,13 @@ impl DirEntry { pub fn metadata(&self) -> io::Result { cfg_select! { // Use directory handle where possible - all( - any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_vendor = "apple", - ), - not(miri) // no dirfd on Miri + any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_vendor = "apple", ) => { let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; From 13b380dbe8c7663516961c877ec1736810054de6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Oct 2026 14:39:05 +1000 Subject: [PATCH 05/17] Several small span improvements Limit changes: - Increase `MAX_CTXT`: it is currently `0b0111_1111_1111_1110` (~15 bits). But it only needs to be distinguishable from `CTXT_INTERNED_MARKER` (all 1s). So we can increase it to `0b1111_1111_1111_1110` (~16 bits). The old limit is rarely reached; the `uom` crate is an exception (it has 39k contexts) and we see a 1-2% instruction count reduction when building it. (This value was incorrectly reduced from ~16 bits to ~15 bits in e525e4f10a8 by me; apologies!) Renamings: - Rename `MAX_CTXT` as `MAX_CTXT_OR_PARENT` because it also relates to the parent field. - Remove the `BASE_` prefix from `BASE_LEN_INTERNED_MARKER` because it doesn't mean anything. Comments: - Update parts of the comment that predate the addition of the `parent` field, such as the frequency measurements. (In incremental builds, 20-40% internment due to non-root context and non-zero parent is common.) - Slightly clarify the meanings of the fields in the different forms, and add a paragraph quickly explaining which specific values distinguish the four forms. --- compiler/rustc_span/src/span_encoding.rs | 118 +++++++++++++---------- 1 file changed, 66 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index af6f219b4e58e..cc67f34571b3b 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -13,69 +13,79 @@ use crate::{BytePos, SPAN_TRACK, SpanData}; /// A compressed span. /// -/// [`SpanData`] is 16 bytes, which is too big to stick everywhere. `Span` only -/// takes up 8 bytes, with less space for the length, parent and context. The -/// vast majority (99.9%+) of `SpanData` instances can be made to fit within -/// those 8 bytes. Any `SpanData` whose fields don't fit into a `Span` are -/// stored in a separate interner table, and the `Span` will index into that -/// table. Interning is rare enough that the cost is low, but common enough -/// that the code is exercised regularly. +/// [`SpanData`] is 16 bytes, which is too big to stick everywhere. `Span` is +/// only 8 bytes. A large majority of `SpanData` instances can be made to fit +/// within those 8 bytes. Any `SpanData` whose fields don't fit into a `Span` +/// are stored in a separate interner table, and the `Span` will index into +/// that table. /// /// An earlier version of this code used only 4 bytes for `Span`, but that was -/// slower because only 80--90% of spans could be stored inline (even less in -/// very large crates) and so the interner was used a lot more. That version of -/// the code also predated the storage of parents. +/// slower because many fewer spans could be stored inline and the interner was +/// used a lot more. That version of the code also predated the storage of +/// parents. +/// +/// Experiments with uncompressed spans yielded worse performance in most cases +/// because memory usage and cache miss rates are significantly higher without +/// compression. /// /// There are four different span forms. /// -/// Inline-context format (requires non-huge length, non-huge context, and no parent): -/// - `span.lo_or_index == span_data.lo` -/// - `span.len_with_tag_or_marker == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`) -/// - `span.ctxt_or_parent_or_marker == span_data.ctxt` (must be `<= MAX_CTXT`) +/// Inline-context format (requires ~15-bit length, ~16-bit context, and no parent): +/// - `span.lo_or_index` holds `span_data.lo` +/// - `span.len_with_tag_or_marker` holds `span_data.hi - span_data.lo` (must be `<= MAX_LEN`) +/// - `span.ctxt_or_parent_or_marker` holds `span_data.ctxt` (must be `<= MAX_CTXT_OR_PARENT`) /// -/// Inline-parent format (requires non-huge length, root context, and non-huge parent): -/// - `span.lo_or_index == span_data.lo` -/// - `span.len_with_tag_or_marker & !PARENT_TAG == len == span_data.hi - span_data.lo` -/// (must be `<= MAX_LEN`) -/// - `span.len_with_tag_or_marker` has top bit (`PARENT_TAG`) set -/// - `span.ctxt_or_parent_or_marker == span_data.parent` (must be `<= MAX_CTXT`) +/// Inline-parent format (requires ~15-bit length, root context, and ~16-bit parent): +/// - `span.lo_or_index` holds `span_data.lo` +/// - `span.len_with_tag_or_marker` holds `PARENT_TAG | (span_data.hi - span_data.lo)` +/// (the len part must be `<= MAX_LEN`) +/// - `span.ctxt_or_parent_or_marker` holds `span_data.parent` (must be `<= MAX_CTXT_OR_PARENT`) /// -/// Partially-interned format (requires non-huge context): -/// - `span.lo_or_index == index` (indexes into the interner table) -/// - `span.len_with_tag_or_marker == BASE_LEN_INTERNED_MARKER` -/// - `span.ctxt_or_parent_or_marker == span_data.ctxt` (must be `<= MAX_CTXT`) +/// Partially-interned format (requires ~16-bit context): +/// - `span.lo_or_index` holds the index into the interner table +/// - `span.len_with_tag_or_marker` is `LEN_INTERNED_MARKER` (all 1s) +/// - `span.ctxt_or_parent_or_marker` holds `span_data.ctxt` (must be `<= MAX_CTXT_OR_PARENT`) +/// - Requires looking in the interning table for lo and length, but the +/// context is stored inline as well as interned because context lookups are +/// often done in isolation. /// /// Fully-interned format (all cases not covered above): -/// - `span.lo_or_index == index` (indexes into the interner table) -/// - `span.len_with_tag_or_marker == BASE_LEN_INTERNED_MARKER` -/// - `span.ctxt_or_parent_or_marker == CTXT_INTERNED_MARKER` +/// - `span.lo_or_index` holds the index into the interner table +/// - `span.len_with_tag_or_marker` is `LEN_INTERNED_MARKER` (all 1s) +/// - `span.ctxt_or_parent_or_marker` is `CTXT_INTERNED_MARKER` (all 1s) /// -/// The partially-interned form requires looking in the interning table for -/// lo and length, but the context is stored inline as well as interned. -/// This is useful because context lookups are often done in isolation, and -/// inline lookups are quicker. +/// In short (see `match_span_kind!` for the code version of this): +/// - The absence/presence of `LEN_INTERNED_MARKER` in +/// `len_with_tag_or_marker` distinguishes the inline forms from the interned +/// forms. +/// - The absence/presence of the `PARENT_TAG` bit in +/// `len_with_tag_or_marker` distinguishes inline-context from inline-parent. +/// - The absence/presence of `CTXT_INTERNED_MARKER` in +/// `ctxt_or_parent_or_marker` distinguishes partially-interned from +/// fully-interned. /// /// Notes about the choice of field sizes: +/// /// - `lo` is 32 bits in both `Span` and `SpanData`, which means that `lo` /// values never cause interning. The number of bits needed for `lo` /// depends on the crate size. 32 bits allows up to 4 GiB of code in a crate. /// Having no compression on this field means there is no performance cliff /// if a crate exceeds a particular size. +/// /// - `len` is ~15 bits in `Span` (a u16, minus 1 bit for PARENT_TAG) and 32 /// bits in `SpanData`, which means that large `len` values will cause /// interning. The number of bits needed for `len` does not depend on the /// crate size. The most common numbers of bits for `len` are from 0 to 7, /// with a peak usually at 3 or 4, and then it drops off quickly from 8 -/// onwards. 15 bits is enough for 99.99%+ of cases, but larger values +/// onwards. 15 bits is enough for 99.9%+ of cases, but larger values /// (sometimes 20+ bits) might occur dozens of times in a typical crate. +/// /// - `ctxt_or_parent_or_marker` is 16 bits in `Span` and two 32 bit fields in -/// `SpanData`, which means intering will happen if `ctxt` is large, if +/// `SpanData`, which means interning will happen if `ctxt` is large, if /// `parent` is large, or if both values are non-zero. The number of bits /// needed for `ctxt` values depend partly on the crate size and partly on -/// the form of the code. No crates in `rustc-perf` need more than 15 bits -/// for `ctxt_or_parent_or_marker`, but larger crates might need more than 16 -/// bits. The number of bits needed for `parent` hasn't been measured, -/// because `parent` isn't currently used by default. +/// the form of the code. No crates in `rustc-perf` need more than 16 bits +/// for `ctxt`, but larger crates might. The same is true for `parent`. /// /// In order to reliably use parented spans in incremental compilation, /// accesses to `lo` and `hi` must introduce a dependency to the parent definition's span. @@ -181,7 +191,7 @@ impl PartiallyInterned { #[inline] fn span(index: u32, ctxt: u16) -> Span { let (lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker) = - (index, BASE_LEN_INTERNED_MARKER, ctxt); + (index, LEN_INTERNED_MARKER, ctxt); Span { lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker } } #[inline] @@ -198,7 +208,7 @@ impl Interned { #[inline] fn span(index: u32) -> Span { let (lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker) = - (index, BASE_LEN_INTERNED_MARKER, CTXT_INTERNED_MARKER); + (index, LEN_INTERNED_MARKER, CTXT_INTERNED_MARKER); Span { lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker } } #[inline] @@ -218,7 +228,7 @@ macro_rules! match_span_kind { PartiallyInterned($span3:ident) => $arm3:expr, Interned($span4:ident) => $arm4:expr, ) => { - if $span.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER { + if $span.len_with_tag_or_marker != LEN_INTERNED_MARKER { if $span.len_with_tag_or_marker & PARENT_TAG == 0 { // Inline-context format. let $span1 = InlineCtxt::from_span($span); @@ -241,11 +251,14 @@ macro_rules! match_span_kind { } // `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from -// `BASE_LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.) +// `LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.) const MAX_LEN: u32 = 0b0111_1111_1111_1110; -const MAX_CTXT: u32 = 0b0111_1111_1111_1110; const PARENT_TAG: u16 = 0b1000_0000_0000_0000; -const BASE_LEN_INTERNED_MARKER: u16 = 0b1111_1111_1111_1111; +const LEN_INTERNED_MARKER: u16 = 0b1111_1111_1111_1111; + +// `MAX_CTXT_OR_PARENT` is chosen so it's as big as possible while not equal to +// `CTXT_INTERNED_MARKER`. +const MAX_CTXT_OR_PARENT: u32 = 0b1111_1111_1111_1110; const CTXT_INTERNED_MARKER: u16 = 0b1111_1111_1111_1111; /// The dummy span has zero position, length, and context, and no parent. @@ -266,12 +279,12 @@ impl Span { // Small len and ctxt may enable one of fully inline formats (or may not). let (len, ctxt32) = (hi.0 - lo.0, ctxt.as_u32()); - if len <= MAX_LEN && ctxt32 <= MAX_CTXT { + if len <= MAX_LEN && ctxt32 <= MAX_CTXT_OR_PARENT { match parent { None => return InlineCtxt::span(lo.0, len as u16, ctxt32 as u16), Some(parent) => { let parent32 = parent.local_def_index.as_u32(); - if ctxt32 == 0 && parent32 <= MAX_CTXT { + if ctxt32 == 0 && parent32 <= MAX_CTXT_OR_PARENT { return InlineParent::span(lo.0, len as u16, parent32 as u16); } } @@ -282,7 +295,7 @@ impl Span { let index = |ctxt| { with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent })) }; - if ctxt32 <= MAX_CTXT { + if ctxt32 <= MAX_CTXT_OR_PARENT { // Interned ctxt should never be read, so it can use any value. PartiallyInterned::span(index(SyntaxContext::from_u32(u32::MAX)), ctxt32 as u16) } else { @@ -335,7 +348,7 @@ impl Span { /// Returns `true` if this is a dummy span with any hygienic context. #[inline] pub fn is_dummy(self) -> bool { - if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER { + if self.len_with_tag_or_marker != LEN_INTERNED_MARKER { // Inline-context or inline-parent format. let lo = self.lo_or_index; let len = (self.len_with_tag_or_marker & !PARENT_TAG) as u32; @@ -358,7 +371,7 @@ impl Span { // so it makes sense to micro-optimize it to avoid `span.data()` and `Span::new()`. let new_ctxt = map(SyntaxContext::from_u16(span.ctxt)); let new_ctxt32 = new_ctxt.as_u32(); - return if new_ctxt32 <= MAX_CTXT { + return if new_ctxt32 <= MAX_CTXT_OR_PARENT { // Any small new context including zero will preserve the format. InlineCtxt::span(span.lo, span.len, new_ctxt32 as u16) } else { @@ -399,9 +412,10 @@ impl Span { pub fn eq_ctxt(self, other: Span) -> bool { match (self.inline_ctxt(), other.inline_ctxt()) { (Ok(ctxt1), Ok(ctxt2)) => ctxt1 == ctxt2, - // If `inline_ctxt` returns `Ok` the context is <= MAX_CTXT. - // If it returns `Err` the span is fully interned and the context is > MAX_CTXT. - // As these do not overlap an `Ok` and `Err` result cannot have an equal context. + // If `inline_ctxt` returns `Ok` the context is <= MAX_CTXT_OR_PARENT. + // If it returns `Err` the span is fully interned and the context + // is > MAX_CTXT_OR_PARENT. As these do not overlap an `Ok` and `Err` result cannot + // have an equal context. (Ok(_), Err(_)) | (Err(_), Ok(_)) => false, (Err(index1), Err(index2)) => with_span_interner(|interner| { interner.spans[index1].ctxt == interner.spans[index2].ctxt @@ -421,7 +435,7 @@ impl Span { None => return self, Some(parent) => { let parent32 = parent.local_def_index.as_u32(); - if span.ctxt == 0 && parent32 <= MAX_CTXT { + if span.ctxt == 0 && parent32 <= MAX_CTXT_OR_PARENT { return InlineParent::span(span.lo, span.len, parent32 as u16); } } From 29df41c47187f735942d13900b784962ef1bc140 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 1 Oct 2026 17:51:47 +0200 Subject: [PATCH 06/17] fix `ValidateBoundVars` --- compiler/rustc_type_ir/src/binder.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 6722adfe24d5d..383e394c3a9e7 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -241,10 +241,10 @@ impl TypeVisitor for ValidateBoundVars { } fn visit_ty(&mut self, t: I::Ty) -> Self::Result { - if t.outer_exclusive_binder() < self.binder_index + if t.outer_exclusive_binder() <= self.binder_index || !self.visited.insert((self.binder_index, t)) { - return ControlFlow::Break(()); + return ControlFlow::Continue(()); } match t.kind() { ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty) @@ -263,8 +263,8 @@ impl TypeVisitor for ValidateBoundVars { } fn visit_const(&mut self, c: Const) -> Self::Result { - if c.outer_exclusive_binder() < self.binder_index { - return ControlFlow::Break(()); + if c.outer_exclusive_binder() <= self.binder_index { + return ControlFlow::Continue(()); } match c.kind() { ty::ConstKind::Bound(debruijn, bound_const) From b533b2617d100235c1c5398cf778b7d706bf31d8 Mon Sep 17 00:00:00 2001 From: landsharkiest Date: Fri, 2 Oct 2026 03:47:42 +0000 Subject: [PATCH 07/17] Add mul_add_relaxed methods for floating-point types --- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 12 +++ library/core/src/num/f128.rs | 39 +++++++++ library/core/src/num/f16.rs | 36 ++++++++ library/core/src/num/f32.rs | 41 +++++++++ library/core/src/num/f64.rs | 44 ++++++++++ tests/ui/intrinsics/float-mul-add-relaxed.rs | 87 ++++++++++++++++++++ 7 files changed, 260 insertions(+) create mode 100644 tests/ui/intrinsics/float-mul-add-relaxed.rs diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e62ad42875248..78b417f13bde7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -996,6 +996,7 @@ symbols! { fields, file, final_associated_functions, + float_mul_add_relaxed, float_to_int_unchecked, floorf16, floorf32, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index ecc8b66ec5785..2f6a125cd5d79 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1404,6 +1404,9 @@ pub const fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +/// +/// The stabilized version of this intrinsic is +/// [`f16::mul_add_relaxed`](../../std/primitive.f16.html#method.mul_add_relaxed) #[inline] #[rustc_intrinsic] #[rustc_nounwind] @@ -1420,6 +1423,9 @@ pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16 { /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +/// +/// The stabilized version of this intrinsic is +/// [`f32::mul_add_relaxed`](../../std/primitive.f32.html#method.mul_add_relaxed) #[inline] #[rustc_intrinsic] #[rustc_nounwind] @@ -1436,6 +1442,9 @@ pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32 { /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +/// +/// The stabilized version of this intrinsic is +/// [`f64::mul_add_relaxed`](../../std/primitive.f64.html#method.mul_add_relaxed) #[inline] #[rustc_intrinsic] #[rustc_nounwind] @@ -1452,6 +1461,9 @@ pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64 { /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +/// +/// The stabilized version of this intrinsic is +/// [`f128::mul_add_relaxed`](../../std/primitive.f128.html#method.mul_add_relaxed) #[inline] #[rustc_intrinsic] #[rustc_nounwind] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index db05d7fdc5087..25ad64a12002c 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1997,6 +1997,45 @@ impl f128 { intrinsics::fmaf128(self, a, b) } + /// Computes `(self * a) + b` with nondeterministic rounding. + /// + /// This is similar to [`mul_add`](Self::mul_add), but the intermediate + /// result may be rounded differently depending on the implementation. + /// The operation is either executed as a single fused multiply-add + /// instruction, or as separate multiply and add instructions. + /// + /// The choice of which one is used is unspecified and non-deterministic: + /// it may vary by target, optimization level, and surrounding code, and + /// even two invocations of this operation with the same inputs may + /// produce different results. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_mul_add_relaxed)] + /// # #[cfg(any(miri, target_has_reliable_f128_math))] { // Miri uses softfloats, always works + /// + /// // When the fused and unfused operations round differently, either + /// // result may be returned: + /// // - 7.824090399073145653039910391751267e-37 is the fused result (one rounding) + /// // - 1.5046327690525280101999827676444745e-36 is the unfused result (two roundings) + /// let r = 0.1_f128.mul_add_relaxed(0.1_f128, -0.01_f128); + /// assert!( + /// r == 7.824090399073145653039910391751267e-37 + /// || r == 1.5046327690525280101999827676444745e-36 + /// ); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "fmuladd")] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn mul_add_relaxed(self, a: f128, b: f128) -> f128 { + intrinsics::fmuladdf128(self, a, b) + } + /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 273ef3688ca5f..7147802486d62 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1983,6 +1983,42 @@ impl f16 { intrinsics::fmaf16(self, a, b) } + /// Computes `(self * a) + b` with nondeterministic rounding. + /// + /// This is similar to [`mul_add`](Self::mul_add), but the intermediate + /// result may be rounded differently depending on the implementation. + /// The operation is either executed as a single fused multiply-add + /// instruction, or as separate multiply and add instructions. + /// + /// The choice of which one is used is unspecified and non-deterministic: + /// it may vary by target, optimization level, and surrounding code, and + /// even two invocations of this operation with the same inputs may + /// produce different results. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_mul_add_relaxed)] + /// # #[cfg(target_has_reliable_f16)] { + /// + /// // When the fused and unfused operations round differently, either + /// // result may be returned: + /// // - -7.03e-6 is the fused result (one rounding) + /// // - -7.6e-6 is the unfused result (two roundings) + /// let r = 0.1_f16.mul_add_relaxed(0.1_f16, -0.01_f16); + /// assert!(r == -7.03e-6 || r == -7.6e-6); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "fmuladd")] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn mul_add_relaxed(self, a: f16, b: f16) -> f16 { + intrinsics::fmuladdf16(self, a, b) + } + /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index d3dea38dc2fcf..e2d13f8f12f2a 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1880,6 +1880,47 @@ impl f32 { intrinsics::frem_algebraic(self, rhs) } + /// Computes `(self * a) + b` with nondeterministic rounding. + /// + /// This is similar to [`mul_add`], but the intermediate result may be + /// rounded differently depending on the implementation. The operation is + /// either executed as a single fused multiply-add instruction, or as + /// separate multiply and add instructions. + /// + /// The choice of which one is used is unspecified and non-deterministic: + /// it may vary by target, optimization level, and surrounding code, and + /// even two invocations of this operation with the same inputs may + /// produce different results. + /// + /// # Precision + /// + /// The result of this operation is not guaranteed: it is either the result + /// of [`mul_add`] (one rounding of the infinite-precision result) or of + /// `self * a + b` (two roundings, with an intermediate rounding of the + /// product). + /// + /// # Examples + /// + /// ``` + /// #![feature(float_mul_add_relaxed)] + /// + /// // When the fused and unfused operations round differently, either + /// // result may be returned: + /// // - 5.2154064e-10 is the fused result (one rounding) + /// // - 9.313226e-10 is the unfused result (two roundings) + /// let r = 0.1_f32.mul_add_relaxed(0.1_f32, -0.01_f32); + /// assert!(r == 5.2154064e-10 || r == 9.313226e-10); + /// ``` + /// + /// [`mul_add`]: ../std/primitive.f32.html#method.mul_add + #[must_use = "method returns a new number and does not mutate the original value"] + #[doc(alias = "fmuladd")] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + pub const fn mul_add_relaxed(self, a: f32, b: f32) -> f32 { + intrinsics::fmuladdf32(self, a, b) + } + /// Returns `self` if the value is not NaN, otherwise returns `replacement` /// if `self` is NaN. /// diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 7c5082749cd11..001172d9b84cb 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1858,6 +1858,50 @@ impl f64 { intrinsics::frem_algebraic(self, rhs) } + /// Computes `(self * a) + b` with nondeterministic rounding. + /// + /// This is similar to [`mul_add`], but the intermediate result may be + /// rounded differently depending on the implementation. The operation is + /// either executed as a single fused multiply-add instruction, or as + /// separate multiply and add instructions. + /// + /// The choice of which one is used is unspecified and non-deterministic: + /// it may vary by target, optimization level, and surrounding code, and + /// even two invocations of this operation with the same inputs may + /// produce different results. + /// + /// # Precision + /// + /// The result of this operation is not guaranteed: it is either the result + /// of [`mul_add`] (one rounding of the infinite-precision result) or of + /// `self * a + b` (two roundings, with an intermediate rounding of the + /// product). + /// + /// # Examples + /// + /// ``` + /// #![feature(float_mul_add_relaxed)] + /// + /// // When the fused and unfused operations round differently, either + /// // result may be returned: + /// // - 9.020562075079397e-19 is the fused result (one rounding) + /// // - 1.734723475976807e-18 is the unfused result (two roundings) + /// # // FIXME(#114479): on `i586`, x87 excess precision gives neither allowed result + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse2"))))] { + /// let r = 0.1_f64.mul_add_relaxed(0.1_f64, -0.01_f64); + /// assert!(r == 9.020562075079397e-19 || r == 1.734723475976807e-18); + /// # } + /// ``` + /// + /// [`mul_add`]: ../std/primitive.f64.html#method.mul_add + #[must_use = "method returns a new number and does not mutate the original value"] + #[doc(alias = "fmuladd")] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + pub const fn mul_add_relaxed(self, a: f64, b: f64) -> f64 { + intrinsics::fmuladdf64(self, a, b) + } + /// Returns `self` if the value is not NaN, otherwise returns `replacement` /// if `self` is NaN. /// diff --git a/tests/ui/intrinsics/float-mul-add-relaxed.rs b/tests/ui/intrinsics/float-mul-add-relaxed.rs new file mode 100644 index 0000000000000..5ab9d52a88d48 --- /dev/null +++ b/tests/ui/intrinsics/float-mul-add-relaxed.rs @@ -0,0 +1,87 @@ +//@ run-pass +//@ compile-flags: -O + +// Check that `mul_add_relaxed` returns either the fused result (one rounding) +// or the unfused result (two roundings), including with optimizations enabled +// where the operation may be const-folded or lowered to a fused instruction. + +#![feature(float_mul_add_relaxed)] +#![feature(f16)] +#![feature(f128)] +#![feature(cfg_target_has_reliable_f16_f128)] +// `f16`/`f128` go unused on targets without reliable f16/f128 math, where the +// gated test functions below are compiled out. +#![allow(unused_features)] +// `target_has_reliable_*` are not "known" configs since they are unstable. +#![expect(unexpected_cfgs)] + +use std::assert_matches; +use std::hint::black_box; + +// On x86 without SSE2 (e.g. the i586 target), floating-point math is evaluated +// with x87 excess precision, so an inexact `mul_add_relaxed` may round to a +// value that is neither the `fN`-precise fused nor unfused result. Skip the +// exact-value checks there. +const EXCESS_PRECISION: bool = cfg!(all(target_arch = "x86", not(target_feature = "sse2"))); + +fn main() { + test_f32(); + test_f64(); + #[cfg(target_has_reliable_f16_math)] + test_f16(); + #[cfg(target_has_reliable_f128_math)] + test_f128(); +} + +fn test_f32() { + // Exactly representable results are the same whether or not the + // operation is fused. + assert_eq!(black_box(2.0_f32).mul_add_relaxed(3.0, 4.0), 10.0); + assert_eq!(black_box(1.0_f32).mul_add_relaxed(1.0, 1.0), 2.0); + + // `0.1 * 0.1` is inexact, so the fused (one rounding) and unfused (two + // roundings) results differ; either is allowed. + if !EXCESS_PRECISION { + let r = black_box(0.1_f32).mul_add_relaxed(0.1, -0.01); + assert_matches!(r, 5.2154064e-10 | 9.313226e-10); + } + + // Edge cases behave like `a * b + c` regardless of fusion. + assert!(black_box(f32::NAN).mul_add_relaxed(1.0, 1.0).is_nan()); + assert_eq!(black_box(f32::INFINITY).mul_add_relaxed(2.0, 1.0), f32::INFINITY); + assert!(black_box(0.0_f32).mul_add_relaxed(f32::INFINITY, 1.0).is_nan()); +} + +fn test_f64() { + assert_eq!(black_box(2.0_f64).mul_add_relaxed(3.0, 4.0), 10.0); + assert_eq!(black_box(1.0_f64).mul_add_relaxed(1.0, 1.0), 2.0); + + if !EXCESS_PRECISION { + let r = black_box(0.1_f64).mul_add_relaxed(0.1, -0.01); + assert_matches!(r, 9.020562075079397e-19 | 1.734723475976807e-18); + } + + assert!(black_box(f64::NAN).mul_add_relaxed(1.0, 1.0).is_nan()); + assert_eq!(black_box(f64::INFINITY).mul_add_relaxed(2.0, 1.0), f64::INFINITY); + assert!(black_box(0.0_f64).mul_add_relaxed(f64::INFINITY, 1.0).is_nan()); +} + +#[cfg(target_has_reliable_f16_math)] +fn test_f16() { + assert_eq!(black_box(2.0_f16).mul_add_relaxed(3.0, 4.0), 10.0); + assert_eq!(black_box(1.0_f16).mul_add_relaxed(1.0, 1.0), 2.0); + + assert!(black_box(f16::NAN).mul_add_relaxed(1.0, 1.0).is_nan()); + assert_eq!(black_box(f16::INFINITY).mul_add_relaxed(2.0, 1.0), f16::INFINITY); + assert!(black_box(0.0_f16).mul_add_relaxed(f16::INFINITY, 1.0).is_nan()); +} + +#[cfg(target_has_reliable_f128_math)] +fn test_f128() { + assert_eq!(black_box(2.0_f128).mul_add_relaxed(3.0, 4.0), 10.0); + assert_eq!(black_box(1.0_f128).mul_add_relaxed(1.0, 1.0), 2.0); + + assert!(black_box(f128::NAN).mul_add_relaxed(1.0, 1.0).is_nan()); + assert_eq!(black_box(f128::INFINITY).mul_add_relaxed(2.0, 1.0), f128::INFINITY); + assert!(black_box(0.0_f128).mul_add_relaxed(f128::INFINITY, 1.0).is_nan()); +} From 6d70bb1e20f9f9fb2c8e2b6950f137b3a51da5b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 2 Oct 2026 08:47:06 +0200 Subject: [PATCH 08/17] bump rustc-build-sysroot --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6866b2c8e4d92..49ab9451ba5e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3493,9 +3493,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "569d545953ee9a1ab9d3e9112e961739ff0cfd50bce06b126937395940be69c9" +checksum = "0d0ed7817483b026efc0492f4ccca177f00fb711537f37943cc412bbf12682b4" dependencies = [ "anyhow", "rustc_version", From d85a18bef9c79b4a4bef25d9044c8bd1b6ab1ab8 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 15 Sep 2026 02:31:59 +0900 Subject: [PATCH 09/17] Partially revert previous wrong fix and do some hack instead --- .../src/solve/fulfill.rs | 25 +++------ .../src/solve/fulfill/derive_errors.rs | 52 +++++++------------ ...ket-ambig-on-nonrigid-assoc-next-solver.rs | 23 ++++++++ 3 files changed, 48 insertions(+), 52 deletions(-) create mode 100644 tests/rustdoc-json/impls/blanket-ambig-on-nonrigid-assoc-next-solver.rs diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 7d3c0a4a6c1f1..5e834c8c4059d 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -18,7 +18,7 @@ use self::derive_errors::*; use super::Certainty; use super::delegate::SolverDelegate; use crate::error_reporting::InferCtxtErrorExt; -use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError}; +use crate::traits::{FulfillmentError, ScrubbedTraitError}; mod derive_errors; @@ -157,8 +157,7 @@ where // the other case. TraitErrors::NoErrors } else { - let errors = collect_remaining_errors_impl(self, infcx); - TraitErrors::from_iter(errors.into_iter()) + TraitErrors::HasErrors(collect_remaining_errors_impl(self, infcx)) } } @@ -367,26 +366,14 @@ where cx.obligations .pending .drain(..) - .filter_map(|(obligation, _)| { - try_ambiguity_error_for_stalled(infcx, obligation).map(NextSolverError::Ambiguity) - }) + .map(|(obligation, _)| NextSolverError::Ambiguity(obligation)) .map(|e| E::from_solver_error(infcx, e)) .collect() } -// We evaluate stalled obligations while collecting remaining errors because a -// previously ambiguous goal may have become successful. In that case we emit a -// delayed bug instead of producing a fulfillment error. Store the diagnostic -// information here so error conversion does not reevaluate the goal. -pub struct NextSolverAmbiguityError<'tcx> { - root_obligation: PredicateObligation<'tcx>, - code: FulfillmentErrorCode<'tcx>, - refine_obligation: bool, -} - pub enum NextSolverError<'tcx> { TrueError(PredicateObligation<'tcx>), - Ambiguity(NextSolverAmbiguityError<'tcx>), + Ambiguity(PredicateObligation<'tcx>), } impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> { @@ -395,8 +382,8 @@ impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tc NextSolverError::TrueError(obligation) => { fulfillment_error_for_no_solution(infcx, obligation) } - NextSolverError::Ambiguity(ambiguity) => { - fulfillment_error_for_stalled(infcx, ambiguity) + NextSolverError::Ambiguity(obligation) => { + fulfillment_error_for_stalled(infcx, obligation) } } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 5ccd7ff7ef55b..7df3fa41a1df2 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -14,7 +14,6 @@ use rustc_next_trait_solver::solve::{GoalEvaluation, MaybeInfo, SolverDelegateEv use rustc_span::{bug, span_bug}; use tracing::{instrument, trace}; -use super::NextSolverAmbiguityError; use crate::solve::delegate::SolverDelegate; use crate::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor}; use crate::solve::{Certainty, deeply_normalize_for_diagnostics}; @@ -84,25 +83,10 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( } pub(super) fn fulfillment_error_for_stalled<'tcx>( - infcx: &InferCtxt<'tcx>, - ambiguity: NextSolverAmbiguityError<'tcx>, -) -> FulfillmentError<'tcx> { - let NextSolverAmbiguityError { root_obligation, code, refine_obligation } = ambiguity; - - let obligation = if refine_obligation { - find_best_leaf_obligation(infcx, &root_obligation, true) - } else { - root_obligation.clone() - }; - - FulfillmentError { obligation, code, root_obligation } -} - -pub(super) fn try_ambiguity_error_for_stalled<'tcx>( infcx: &InferCtxt<'tcx>, root_obligation: PredicateObligation<'tcx>, -) -> Option> { - let evaluation = infcx.probe(|_| { +) -> FulfillmentError<'tcx> { + let (code, refine_obligation) = infcx.probe(|_| { match <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal( root_obligation.as_goal(), root_obligation.cause.span, @@ -116,7 +100,7 @@ pub(super) fn try_ambiguity_error_for_stalled<'tcx>( stalled_on_coroutines: _, }), .. - }) => Some((FulfillmentErrorCode::Ambiguity { overflow: None }, true)), + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeInfo { @@ -126,7 +110,7 @@ pub(super) fn try_ambiguity_error_for_stalled<'tcx>( stalled_on_coroutines: _, }), .. - }) => Some(( + }) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, // Don't look into overflows because we treat overflows weirdly anyways. // We discard the inference constraints from overflowing goals, so @@ -135,17 +119,13 @@ pub(super) fn try_ambiguity_error_for_stalled<'tcx>( // // FIXME: We should probably just look into overflows here. false, - )), + ), Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { - infcx.dcx().span_delayed_bug( - root_obligation.cause.span, - format!( - "did not expect successful goal when collecting ambiguity errors for `{:?}`", - infcx.deeply_resolve_ignoring_regions(root_obligation.predicate), - ), - ); - None - }, + // FIXME: We should ICE here. See the following links for details + // - + // - + (FulfillmentErrorCode::Ambiguity { overflow: None }, false) + } Err(_) => { span_bug!( root_obligation.cause.span, @@ -156,9 +136,15 @@ pub(super) fn try_ambiguity_error_for_stalled<'tcx>( } }); - let (code, refine_obligation) = evaluation?; - - Some(NextSolverAmbiguityError { root_obligation, code, refine_obligation }) + FulfillmentError { + obligation: if refine_obligation { + find_best_leaf_obligation(infcx, &root_obligation, true) + } else { + root_obligation.clone() + }, + code, + root_obligation, + } } #[instrument(level = "debug", skip(infcx), ret)] diff --git a/tests/rustdoc-json/impls/blanket-ambig-on-nonrigid-assoc-next-solver.rs b/tests/rustdoc-json/impls/blanket-ambig-on-nonrigid-assoc-next-solver.rs new file mode 100644 index 0000000000000..8f3af90a2598e --- /dev/null +++ b/tests/rustdoc-json/impls/blanket-ambig-on-nonrigid-assoc-next-solver.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Znext-solver + +// Regression test for + +pub trait Service { + type Future; +} + +pub trait ZebraService: Service {} + +impl ZebraService for MaybeVerify where + MaybeVerify: Service +{ +} + +pub struct Verifier; + +impl Service<()> for Verifier { + type Future = &'static (); +} + +//@ set blanket = "$.index[?(@.inner.impl.blanket_impl.generic=='MaybeVerify')].id" +//@ has "$.index[?(@.name=='Verifier')].inner.struct.impls[*]" $blanket From 3510be9a06e2eedb5be9a3cf7af10d646953bb04 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 2 Oct 2026 07:45:10 +0000 Subject: [PATCH 10/17] Revert "doc: Fix external mention of signum() return value regarding NaNs" This reverts commit 88db34ad12223f211786050877bb6005cfcaa1e6. --- library/core/src/num/f128.rs | 4 ++-- library/core/src/num/f16.rs | 4 ++-- library/core/src/num/f32.rs | 4 ++-- library/core/src/num/f64.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index e53e4df1ea43d..d490ccd26f8e4 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -594,7 +594,7 @@ impl f128 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// #![feature(f128)] @@ -620,7 +620,7 @@ impl f128 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// #![feature(f128)] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 4ad41bd6cfa4b..3d13c2016e9a8 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -588,7 +588,7 @@ impl f16 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// #![feature(f16)] @@ -616,7 +616,7 @@ impl f16 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// #![feature(f16)] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 2dc3ddf31cac5..f48127df6ce9b 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -811,7 +811,7 @@ impl f32 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// let f = 7.0_f32; @@ -836,7 +836,7 @@ impl f32 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// let f = 7.0f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index cd3cfe1bd4c60..0e02b1f912dcd 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -810,7 +810,7 @@ impl f64 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; @@ -835,7 +835,7 @@ impl f64 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; From 5ec3896697c0be2d8f3dc029bab953777022829b Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 2 Oct 2026 07:48:36 +0000 Subject: [PATCH 11/17] Use 'evaluates to' wording --- library/core/src/num/f128.rs | 4 ++-- library/core/src/num/f16.rs | 4 ++-- library/core/src/num/f32.rs | 4 ++-- library/core/src/num/f64.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index d490ccd26f8e4..b0b4dcda72a49 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -594,7 +594,7 @@ impl f128 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return `false` for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f128)] @@ -620,7 +620,7 @@ impl f128 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return `false` for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f128)] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 3d13c2016e9a8..8dff40dd77117 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -588,7 +588,7 @@ impl f16 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return `false` for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f16)] @@ -616,7 +616,7 @@ impl f16 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return `false` for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f16)] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index f48127df6ce9b..0a384a18cab41 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -811,7 +811,7 @@ impl f32 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return `false` for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0_f32; @@ -836,7 +836,7 @@ impl f32 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return `false` for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 0e02b1f912dcd..cc694d332b6a3 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -810,7 +810,7 @@ impl f64 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return `false` for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; @@ -835,7 +835,7 @@ impl f64 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return `false` for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; From 9270acb1bfd9b1b974ebcf92d0cb198622880a79 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 1 Oct 2026 02:07:58 +0000 Subject: [PATCH 12/17] Fix `TypeOutlives` fast-path --- compiler/rustc_trait_selection/src/solve/delegate.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index e1043d53fa6fc..dcfd21297dc76 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -229,6 +229,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< } let ty = self.deeply_resolve_ignoring_regions(outlives.0); + if ty.has_non_rigid_aliases() { + return Outcome::NoFastPath; + } + let mut infer_collector = CollectNonRegionInfer { infers: Default::default(), visited: Default::default(), @@ -247,10 +251,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< ); } - if ty.has_non_rigid_aliases() { - return Outcome::NoFastPath; - } - self.0.register_type_outlives_constraint( outlives.0, outlives.1, From eb4108a9859ebfa071a57b9f7e90cc8815311996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 2 Oct 2026 11:47:13 +0200 Subject: [PATCH 13/17] Add mailmap entry --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index d72c2e39c26a3..33000e11dcefa 100644 --- a/.mailmap +++ b/.mailmap @@ -64,6 +64,7 @@ Arthur Cohen Arthur Silva Arthur Woimbée Artyom Pavlov +Austin K. Austin Seipp Ayaz Hafiz Aydin Kim aydin.kim From c39fbc44eb0f296a7ad161eee642e8bcd2a82235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandra=20Gonz=C3=A1lez?= Date: Fri, 2 Oct 2026 12:00:55 +0200 Subject: [PATCH 14/17] Rename `rustc_driver::run_compiler` to `compiler_entrypoint` Having two `run_compiler` functions has always been confusing and not necessary at all, as they perform fairly different functions. Even more so because `rustc_driver::run_compiler` calls to `rustc_interface::run_compiler`. So it seems like a recursive function when it really isn't. --- compiler/rustc_driver_impl/src/lib.rs | 6 +++--- compiler/rustc_public/src/rustc_internal/mod.rs | 2 +- src/doc/rustc-dev-guide/examples/rustc-driver-example.rs | 4 ++-- .../examples/rustc-driver-interacting-with-the-ast.rs | 4 ++-- src/doc/rustc-dev-guide/src/compiler-debugging.md | 6 +++--- src/doc/rustc-dev-guide/src/rustc-driver/intro.md | 8 ++++---- src/tools/clippy/src/driver.rs | 6 +++--- src/tools/miri/priroda/src/main.rs | 2 +- src/tools/miri/src/bin/miri.rs | 2 +- tests/ui-fulldeps/compiler-calls.rs | 2 +- tests/ui-fulldeps/obtain-borrowck.rs | 2 +- tests/ui-fulldeps/polymorphic-drop-glue.rs | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 7940dc3978496..959222b19c6e9 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -169,7 +169,7 @@ impl Callbacks for TimePassesCallbacks { } /// This is the primary entry point for rustc. -pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) { +pub fn compiler_entrypoint(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) { let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); // Throw away the first argument, the name of the binary. @@ -1386,7 +1386,7 @@ static ICE_PATH: OnceLock> = OnceLock::new(); // This function should only be called from the ICE hook. // -// The intended behavior is that `run_compiler` will invoke `ice_path_with_config` early in the +// The intended behavior is that `compiler_entrypoint` will invoke `ice_path_with_config` early in the // initialization process to properly initialize the ICE_PATH static based on parsed CLI flags. // // Subsequent calls to either function will then return the proper ICE path as configured by @@ -1674,7 +1674,7 @@ pub fn main() -> ExitCode { install_ctrlc_handler(); let exit_code = - catch_with_exit_code(|| run_compiler(&args::raw_args(&early_dcx), &mut callbacks)); + catch_with_exit_code(|| compiler_entrypoint(&args::raw_args(&early_dcx), &mut callbacks)); if let Some(format) = callbacks.time_passes { let end_rss = get_resident_set_size(); diff --git a/compiler/rustc_public/src/rustc_internal/mod.rs b/compiler/rustc_public/src/rustc_internal/mod.rs index 49dc64572923a..744aa1ffdd314 100644 --- a/compiler/rustc_public/src/rustc_internal/mod.rs +++ b/compiler/rustc_public/src/rustc_internal/mod.rs @@ -168,7 +168,7 @@ macro_rules! optional { #[doc(hidden)] macro_rules! run_driver { ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{ - use rustc_driver::{Callbacks, Compilation, run_compiler}; + use rustc_driver::{Callbacks, Compilation, compiler_entrypoint}; use rustc_middle::ty::TyCtxt; use rustc_interface::interface; use rustc_public::rustc_internal; diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index db6ac18578542..55665220c5f89 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -20,7 +20,7 @@ use std::path::Path; use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_driver::{Compilation, run_compiler}; +use rustc_driver::{Compilation, compiler_entrypoint}; use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; @@ -87,7 +87,7 @@ impl rustc_driver::Callbacks for MyCallbacks { } fn main() { - run_compiler( + compile_entrypoint( &[ // The first argument, which in practice contains the name of the binary being executed // (i.e. "rustc") is ignored by rustc. diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index c0d7f977d3500..f3449e830c9b7 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -20,7 +20,7 @@ use std::path::Path; use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_driver::{Compilation, run_compiler}; +use rustc_driver::{Compilation, compiler_entrypoint}; use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; @@ -94,7 +94,7 @@ impl rustc_driver::Callbacks for MyCallbacks { } fn main() { - run_compiler( + compiler_entrypoint( &[ // The first argument, which in practice contains the name of the binary being executed // (i.e. "rustc") is ignored by rustc. diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 79f2145afce1b..3c355c0d3b972 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -75,7 +75,7 @@ stack backtrace: 34: >::with 35: rustc::ty::context::TyCtxt::create_and_enter 36: rustc_driver::driver::compile_input - 37: rustc_driver::run_compiler + 37: rustc_driver::compiler_entrypoint ``` If you set `debug = true`, you will get line numbers for the stack trace. @@ -93,7 +93,7 @@ stack backtrace: 33: rustc_driver::driver::compile_input at /home/user/rust/compiler/rustc_driver/src/driver.rs:1010 at /home/user/rust/compiler/rustc_driver/src/driver.rs:212 - 34: rustc_driver::run_compiler + 34: rustc_driver::compiler_entrypoint at /home/user/rust/compiler/rustc_driver/src/lib.rs:253 ``` @@ -181,7 +181,7 @@ stack backtrace: 9: rustc_typeck::check::FnCtxt::select_obligations_where_possible at /home/user/rust/compiler/rustc_typeck/src/check/mod.rs:2192 (~~~ IRRELEVANT PART OF BACKTRACE REMOVED BY ME ~~~) - 36: rustc_driver::run_compiler + 36: rustc_driver::compiler_entrypoint at /home/user/rust/compiler/rustc_driver/src/lib.rs:253 ``` diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md index 32d27d218197d..687b2d49d094a 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md @@ -6,7 +6,7 @@ The [`rustc_driver`] is essentially `rustc`'s `main` function. It acts as the glue for running the various phases of the compiler in the correct order, using the interface defined in the [`rustc_interface`] crate. Where possible, using [`rustc_driver`] rather than [`rustc_interface`] is recommended. -The main entry point of [`rustc_driver`] is [`rustc_driver::run_compiler`][rd_rc]. +The main entry point of [`rustc_driver`] is [`rustc_driver::compiler_entrypoint`][rd_ce]. This builder accepts the same command-line args as rustc as well as an implementation of [`Callbacks`] and a couple of other optional options. [`Callbacks`] is a `trait` that allows for custom compiler configuration, as well as allowing custom code to run after different phases of the compilation. @@ -23,7 +23,7 @@ Inside the `closure` you can use the `Compiler` to call various functions to com You can see a minimal example of how to use [`rustc_interface`] [here][example]. You can see an example of how to use the various functions using [`rustc_interface`] needs by looking at the `rustc_driver` implementation, -specifically [`rustc_driver_impl::run_compiler`][rdi_rc] +specifically [`rustc_driver_impl::compiler_entrypoint`][rdi_ce] (not to be confused with [`rustc_interface::run_compiler`][i_rc]). > **Warning:** By its very nature, the internal compiler APIs are always going @@ -36,5 +36,5 @@ specifically [`rustc_driver_impl::run_compiler`][rdi_rc] [`Callbacks`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html [example]: https://github.com/rust-lang/rustc-dev-guide/blob/main/examples/rustc-interface-example.rs [i_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/fn.run_compiler.html -[rd_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.run_compiler.html -[rdi_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver_impl/fn.run_compiler.html +[rd_ce]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.compiler_entrypoint.html +[rdi_ce]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver_impl/fn.compiler_entrypoint.html diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 16c3a6434f35a..e83fe592a7ca6 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -243,7 +243,7 @@ fn main() -> ExitCode { let mut args: Vec = orig_args.clone(); pass_sysroot_env_if_given(&mut args, sys_root_env); - rustc_driver::run_compiler(&args, &mut DefaultCallbacks); + rustc_driver::compiler_entrypoint(&args, &mut DefaultCallbacks); return ExitCode::SUCCESS; } @@ -304,9 +304,9 @@ fn main() -> ExitCode { let clippy_enabled = !cap_lints_allow && relevant_package && !info_query; if clippy_enabled { args.extend(clippy_args); - rustc_driver::run_compiler(&args, &mut ClippyCallbacks { clippy_args_var }); + rustc_driver::compiler_entrypoint(&args, &mut ClippyCallbacks { clippy_args_var }); } else { - rustc_driver::run_compiler(&args, &mut RustcCallbacks { clippy_args_var }); + rustc_driver::compiler_entrypoint(&args, &mut RustcCallbacks { clippy_args_var }); } ExitCode::SUCCESS }) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 3937b9ad03f4d..6fc62b4d6e04b 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -41,7 +41,7 @@ fn main() { args.push(find_sysroot()); } // FIXME: handle the same `-Z` flags that Miri accepts. - rustc_driver::run_compiler(&args, &mut PrirodaCompilerCalls::new(frontend)); + rustc_driver::compiler_entrypoint(&args, &mut PrirodaCompilerCalls::new(frontend)); } /// Frontend selected by Priroda-specific CLI flags. diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 7b3f166b3bf87..7c5bda3d628ce 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -366,7 +366,7 @@ fn run_compiler_and_exit( // Invoke compiler, catch any unwinding panics and handle return code. let exit_code = - rustc_driver::catch_with_exit_code(move || rustc_driver::run_compiler(args, callbacks)); + rustc_driver::catch_with_exit_code(move || rustc_driver::compiler_entrypoint(args, callbacks)); exit(if exit_code == ExitCode::SUCCESS { rustc_driver::EXIT_SUCCESS } else { diff --git a/tests/ui-fulldeps/compiler-calls.rs b/tests/ui-fulldeps/compiler-calls.rs index d6148dfec43ce..ea91f23213e1c 100644 --- a/tests/ui-fulldeps/compiler-calls.rs +++ b/tests/ui-fulldeps/compiler-calls.rs @@ -25,7 +25,7 @@ fn main() { let mut count = 1; let args = vec!["compiler-calls".to_string(), "foo.rs".to_string()]; rustc_driver::catch_fatal_errors(|| -> interface::Result<()> { - rustc_driver::run_compiler(&args, &mut TestCalls { count: &mut count }); + rustc_driver::compiler_entrypoint(&args, &mut TestCalls { count: &mut count }); Ok(()) }) .ok(); diff --git a/tests/ui-fulldeps/obtain-borrowck.rs b/tests/ui-fulldeps/obtain-borrowck.rs index d4a74d3b7dec8..950770d91fad1 100644 --- a/tests/ui-fulldeps/obtain-borrowck.rs +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -50,7 +50,7 @@ fn main() -> ExitCode { rustc_args.push("-Zpolonius".to_owned()); let mut callbacks = CompilerCalls::default(); // Call the Rust compiler with our callbacks. - rustc_driver::run_compiler(&rustc_args, &mut callbacks); + rustc_driver::compiler_entrypoint(&rustc_args, &mut callbacks); }) } diff --git a/tests/ui-fulldeps/polymorphic-drop-glue.rs b/tests/ui-fulldeps/polymorphic-drop-glue.rs index 4d85d342d7395..a3ad05ef228e3 100644 --- a/tests/ui-fulldeps/polymorphic-drop-glue.rs +++ b/tests/ui-fulldeps/polymorphic-drop-glue.rs @@ -44,7 +44,7 @@ fn main() -> ExitCode { args.push("--crate-type=lib".to_owned()); args.push("drop_glue_polymorphic_const_generic_input.rs".to_owned()); - rustc_driver::run_compiler(&args, &mut CompilerCalls); + rustc_driver::compiler_entrypoint(&args, &mut CompilerCalls); }) } From 1859636fd0ac1d7e523d60681d8c93e61582e6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandra=20Gonz=C3=A1lez?= Date: Fri, 2 Oct 2026 11:15:22 +0000 Subject: [PATCH 15/17] Change rustc_public `run` macro to also use `compiler_entrypoint` --- compiler/rustc_public/src/rustc_internal/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_public/src/rustc_internal/mod.rs b/compiler/rustc_public/src/rustc_internal/mod.rs index 744aa1ffdd314..d127a62862e78 100644 --- a/compiler/rustc_public/src/rustc_internal/mod.rs +++ b/compiler/rustc_public/src/rustc_internal/mod.rs @@ -199,7 +199,7 @@ macro_rules! run_driver { /// Runs the compiler against given target and tests it with `test_function` pub fn run(&mut self, args: &[String]) -> Result> { let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { - run_compiler(&args, self); + compiler_entrypoint(&args, self); Ok(()) }); match (compiler_result, self.result.take()) { From 60f8e925ce0bd5fbc9cbdc6a3528885dbc057b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 2 Oct 2026 13:33:42 +0200 Subject: [PATCH 16/17] Remove variants from `feature-gate-autodiff-use` test --- .../feature-gate-autodiff-use.rs | 22 +++++++----------- .../feature-gate-autodiff-use.stable.stderr | 23 ------------------- ...tderr => feature-gate-autodiff-use.stderr} | 4 ++-- 3 files changed, 10 insertions(+), 39 deletions(-) delete mode 100644 tests/ui/feature-gates/feature-gate-autodiff-use.stable.stderr rename tests/ui/feature-gates/{feature-gate-autodiff-use.nightly.stderr => feature-gate-autodiff-use.stderr} (91%) diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.rs b/tests/ui/feature-gates/feature-gate-autodiff-use.rs index d97c06de7de5b..85608be4ac04a 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff-use.rs +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.rs @@ -1,22 +1,16 @@ -//@ revisions: nightly stable -//@[nightly] only-nightly -//@[stable] only-stable - // This checks that without enabling the autodiff feature, we can't import std::autodiff::autodiff; #![crate_type = "lib"] use std::autodiff::autodiff_reverse; -//[nightly]~^ ERROR use of unstable library feature `autodiff` -//[stable]~^^ ERROR use of unstable library feature `autodiff` -//[stable]~| NOTE see issue #124509 for more information -//[stable]~| HELP add `#![feature(autodiff)]` to the crate attributes to enable -//[stable]~| NOTE this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +//~^ ERROR use of unstable library feature `autodiff` +//| NOTE see issue #124509 for more information +//| HELP add `#![feature(autodiff)]` to the crate attributes to enable +//| NOTE this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date #[autodiff_reverse(dfoo)] -//[nightly]~^ ERROR use of unstable library feature `autodiff` [E0658] -//[stable]~^^ ERROR use of unstable library feature `autodiff` [E0658] -//[stable]~| NOTE see issue #124509 for more information -//[stable]~| HELP add `#![feature(autodiff)]` to the crate attributes to enable -//[stable]~| NOTE this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +//~^ ERROR use of unstable library feature `autodiff` [E0658] +//| NOTE see issue #124509 for more information +//| HELP add `#![feature(autodiff)]` to the crate attributes to enable +//| NOTE this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date fn foo() {} diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.stable.stderr b/tests/ui/feature-gates/feature-gate-autodiff-use.stable.stderr deleted file mode 100644 index ca724632063d4..0000000000000 --- a/tests/ui/feature-gates/feature-gate-autodiff-use.stable.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0658]: use of unstable library feature `autodiff` - --> $DIR/feature-gate-autodiff-use.rs:16:3 - | -LL | #[autodiff_reverse(dfoo)] - | ^^^^^^^^^^^^^^^^ - | - = note: see issue #124509 for more information - = help: add `#![feature(autodiff)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `autodiff` - --> $DIR/feature-gate-autodiff-use.rs:9:5 - | -LL | use std::autodiff::autodiff_reverse; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #124509 for more information - = help: add `#![feature(autodiff)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.nightly.stderr b/tests/ui/feature-gates/feature-gate-autodiff-use.stderr similarity index 91% rename from tests/ui/feature-gates/feature-gate-autodiff-use.nightly.stderr rename to tests/ui/feature-gates/feature-gate-autodiff-use.stderr index ca724632063d4..94b9a065e491e 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff-use.nightly.stderr +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `autodiff` - --> $DIR/feature-gate-autodiff-use.rs:16:3 + --> $DIR/feature-gate-autodiff-use.rs:11:3 | LL | #[autodiff_reverse(dfoo)] | ^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #[autodiff_reverse(dfoo)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `autodiff` - --> $DIR/feature-gate-autodiff-use.rs:9:5 + --> $DIR/feature-gate-autodiff-use.rs:5:5 | LL | use std::autodiff::autodiff_reverse; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 9b60c2ad0f07a0eadedc06fdebd858a9ee8a328b Mon Sep 17 00:00:00 2001 From: Yara Date: Tue, 15 Sep 2026 16:40:04 +0200 Subject: [PATCH 17/17] reflection: move type_id intrinsics to intrinsics::reflection --- .../rustc_hir_analysis/src/check/intrinsic.rs | 11 +- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/any.rs | 9 +- library/core/src/intrinsics/mod.rs | 177 +--------------- library/core/src/intrinsics/reflection.rs | 194 ++++++++++++++++++ library/core/src/mem/type_info.rs | 39 ++-- library/core/src/unit.rs | 2 +- library/coretests/tests/intrinsics.rs | 2 +- tests/mir-opt/inline/type_id_eq.rs | 2 +- .../rustc_public/check_allocation.rs | 2 +- 10 files changed, 229 insertions(+), 211 deletions(-) create mode 100644 library/core/src/intrinsics/reflection.rs diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 88a93229296c2..168896a5c8d47 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -198,7 +198,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf64 | sym::truncf128 | sym::type_id - | sym::type_id_non_exhaustive | sym::type_id_array_len | sym::type_id_element_ty | sym::type_id_eq @@ -207,16 +206,16 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_id_function_ptr | sym::type_id_generics | sym::type_id_is_signed + | sym::type_id_non_exhaustive | sym::type_id_points_mutably | sym::type_id_points_to - | sym::type_id_variants - | sym::type_id_vtable - | sym::type_name - | sym::type_id_type_of | sym::type_id_size_of - | sym::type_id_variants + | sym::type_id_type_of | sym::type_id_variant_name | sym::type_id_variant_non_exhaustive + | sym::type_id_variants + | sym::type_id_vtable + | sym::type_name | sym::ub_checks | sym::va_copy | sym::variant_count diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3adfb62d1ce54..abf38d2847fb3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2207,7 +2207,6 @@ symbols! { type_id_points_mutably, type_id_points_to, type_id_size_of, - type_name, type_id_type_of, type_id_variant_name, type_id_variant_non_exhaustive, @@ -2221,6 +2220,7 @@ symbols! { type_ir_interner, type_length_limit, type_macros, + type_name, type_privacy_lints, typed_swap_nonoverlapping, u8, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 85ff2fe1dd6ee..98563f9011f87 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -86,7 +86,8 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::intrinsics::{self, type_id, type_id_vtable}; +use crate::intrinsics::reflection::{type_id, type_id_vtable}; +use crate::intrinsics::{self}; use crate::mem::transmute; use crate::mem::type_info::{TraitImpl, TypeKind}; use crate::{fmt, hash, ptr}; @@ -742,7 +743,7 @@ unsafe impl Sync for TypeId {} const impl PartialEq for TypeId { #[inline] fn eq(&self, other: &Self) -> bool { - crate::intrinsics::type_id_eq(*self, *other) + crate::intrinsics::reflection::type_id_eq(*self, *other) } } @@ -765,7 +766,7 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_type_id", since = "1.91.0")] pub const fn of() -> TypeId { - const { intrinsics::type_id::() } + const { intrinsics::reflection::type_id::() } } /// Checks if the [TypeId] implements the trait. If it does it returns [TraitImpl] which can be used to build a fat pointer. @@ -903,7 +904,7 @@ impl fmt::Debug for TypeId { #[stable(feature = "type_name", since = "1.38.0")] #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub const fn type_name() -> &'static str { - const { intrinsics::type_name::() } + const { intrinsics::reflection::type_name::() } } /// Returns the type name of the pointed-to value as a string slice. diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 12cb3d2b6d8b4..34675bf8fc429 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -88,6 +88,7 @@ pub mod fallback; pub mod gpu; mod macros; pub mod mir; +pub mod reflection; pub mod simd; use macros::intrinsic_dispatch_on_type; @@ -2962,25 +2963,6 @@ pub const unsafe fn size_of_val(ptr: *const T) -> usize; #[rustc_intrinsic_const_stable_indirect] pub const unsafe fn align_of_val(ptr: *const T) -> usize; -#[rustc_intrinsic] -#[rustc_comptime] -#[unstable(feature = "core_intrinsics", issue = "none")] -/// Check if a type represented by a `TypeId` implements a trait represented by a `TypeId`. -/// It can only be called at compile time, the backends do -/// not implement it. If it implements the trait the dyn metadata gets returned for vtable access. -pub fn type_id_vtable( - _id: crate::any::TypeId, - _trait: crate::any::TypeId, -) -> Option>; - -/// Compute the type information of a concrete type. -/// It can only be called at compile time, the backends do -/// not implement it. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type; - /// Gets a static string slice containing the name of a type. /// /// Note that, unlike most intrinsics, this can only be called at compile-time @@ -2995,121 +2977,6 @@ pub fn type_id_type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type; #[rustc_comptime] pub fn type_name() -> &'static str; -/// Gets an identifier which is globally unique to the specified type. This -/// function will return the same value for a type regardless of whichever -/// crate it is invoked in. -/// -/// Note that, unlike most intrinsics, this can only be called at compile-time -/// as backends do not have an implementation for it. The only caller (its -/// stable counterpart) wraps this intrinsic call in a `const` block so that -/// backends only see an evaluated constant. -/// -/// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. -#[rustc_nounwind] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic] -#[rustc_comptime] -pub fn type_id() -> crate::any::TypeId; - -/// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the -/// same type. This is necessary because at const-eval time the actual discriminating -/// data is opaque and cannot be inspected directly. -/// -/// The stabilized version of this intrinsic is the [PartialEq] impl for [`core::any::TypeId`]. -#[rustc_nounwind] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic] -#[rustc_do_not_const_check] -pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool { - // SAFETY: we know `TypeId` is 16 bytes of initialized data. - // This is runtime-only code so we do not have to worry about provenance. - unsafe { crate::mem::transmute::<_, u128>(a) == crate::mem::transmute::<_, u128>(b) } -} - -/// Returns whether the type represented by this `TypeId` is a signed integer. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::is_signed`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_is_signed(_id: crate::any::TypeId) -> bool; - -/// Gets the length of the array represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::array_len`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_array_len(_id: crate::any::TypeId) -> usize; - -/// Gets the type of each element of the array or slice represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::element_ty`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_element_ty(_id: crate::any::TypeId) -> Option; - -/// Gets the size of the type represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::size`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_size_of(_id: crate::any::TypeId) -> Option; - -/// Gets the number of variants of the type represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::variants`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_variants(_id: crate::any::TypeId) -> usize; - -/// Gets the name of the variant represented by the base `TypeId` and variant_idx. -/// -/// The more user-friendly version of this intrinsic is [`core::mem::type_info::VariantId::name`]. -/// -/// [`TypeId`]: crate::any::TypeId -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_variant_name(_base: crate::any::TypeId, _variant_index: usize) -> &'static str; - -/// Returns true when the variant represented by the base `TypeId` and variant_idx is non -/// exhaustive. -/// -/// The more user-friendly version of this intrinsic is -/// [`core::mem::type_info::VariantId::non_exhaustive`]. -/// -/// [`TypeId`]: crate::any::TypeId -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_variant_non_exhaustive(base: crate::any::TypeId, variant: usize) -> bool; - -/// Gets the number of fields at the given `variant_index` represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::fields`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_fields(_id: crate::any::TypeId, _variant_index: usize) -> usize; - -/// Gets the [`FieldRepresentingType`]'s `TypeId` at the given index of the type represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::field`]. -/// -/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_field_representing_type( - _id: crate::any::TypeId, - _variant_index: usize, - _field_index: usize, -) -> crate::any::TypeId; - /// Gets the actual field `TypeId` of the [`FieldRepresentingType`]'s `TypeId`. /// /// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::type_id`]. @@ -3142,48 +3009,6 @@ pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'stati #[rustc_comptime] pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize; -/// Given a `TypeId` that represents a function pointer returns an [`core::mem::type_info::FnPtr`]. -/// When called on something else this returns `None`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::function_ptr`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option; - -/// Checks whether this type is non-exhaustive. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_non_exhaustive(_id: crate::any::TypeId) -> bool; - -/// Returns the list of generic args on this type. -/// Only meaningful for Adts, closures, ... Everything else returns an empty slice. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_info::Generic]; - -// FIXME(reflection): Pick a consistent naming scheme for the intrinsics. Right now we got -// type_id_, _type_id and intrinsics not mentioning type_id at all. -/// Given a `TypeId` that represents a pointer this returns the `TypeId` which that pointer -/// points to. When called on anything else this returns None. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_to`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_points_to(_id: crate::any::TypeId) -> Option; - -/// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. -/// When called on anything else this returns `false`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_mutably`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_points_mutably(_id: crate::any::TypeId) -> bool; - /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/intrinsics/reflection.rs b/library/core/src/intrinsics/reflection.rs new file mode 100644 index 0000000000000..f0e0f9d66e033 --- /dev/null +++ b/library/core/src/intrinsics/reflection.rs @@ -0,0 +1,194 @@ +//! Intrinsics for compile time reflection +//! +//! This are all methods operating on [`TypeId`](core::any::TypeId) which is the +//! entry point to all compile time reflection. + +use crate::ptr; + +/// Gets an identifier which is globally unique to the specified type. This +/// function will return the same value for a type regardless of whichever +/// crate it is invoked in. +/// +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. +/// +/// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_comptime] +pub fn type_id() -> crate::any::TypeId; + +/// Compute the type information of a concrete type. +/// It can only be called at compile time, the backends do +/// not implement it. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type; + +/// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the +/// same type. This is necessary because at const-eval time the actual discriminating +/// data is opaque and cannot be inspected directly. +/// +/// The stabilized version of this intrinsic is the [PartialEq] impl for [`core::any::TypeId`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_do_not_const_check] +pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool { + // SAFETY: we know `TypeId` is 16 bytes of initialized data. + // This is runtime-only code so we do not have to worry about provenance. + unsafe { crate::mem::transmute::<_, u128>(a) == crate::mem::transmute::<_, u128>(b) } +} + +#[rustc_intrinsic] +#[rustc_comptime] +#[unstable(feature = "core_intrinsics", issue = "none")] +/// Check if a type represented by a `TypeId` implements a trait represented by a `TypeId`. +/// It can only be called at compile time, the backends do +/// not implement it. If it implements the trait the dyn metadata gets returned for vtable access. +pub fn type_id_vtable( + _id: crate::any::TypeId, + _trait: crate::any::TypeId, +) -> Option>; + +/// Gets a static string slice containing the name of a type. +/// +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. +/// +/// The stabilized version of this intrinsic is [`core::any::type_name`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_comptime] +pub fn type_name() -> &'static str; + +/// Returns whether the type represented by this `TypeId` is a signed integer. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::is_signed`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_is_signed(_id: crate::any::TypeId) -> bool; + +/// Gets the length of the array represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::array_len`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_array_len(_id: crate::any::TypeId) -> usize; + +/// Gets the type of each element of the array or slice represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::element_ty`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_element_ty(_id: crate::any::TypeId) -> Option; + +/// Gets the size of the type represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::size`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_size_of(_id: crate::any::TypeId) -> Option; + +/// Gets the number of variants of the type represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::variants`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_variants(_id: crate::any::TypeId) -> usize; + +/// Gets the name of the variant represented by the base `TypeId` and variant_idx. +/// +/// The more user-friendly version of this intrinsic is [`core::mem::type_info::VariantId::name`]. +/// +/// [`TypeId`]: crate::any::TypeId +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_variant_name(_base: crate::any::TypeId, _variant_index: usize) -> &'static str; + +/// Returns true when the variant represented by the base `TypeId` and variant_idx is non +/// exhaustive. +/// +/// The more user-friendly version of this intrinsic is +/// [`core::mem::type_info::VariantId::non_exhaustive`]. +/// +/// [`TypeId`]: crate::any::TypeId +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_variant_non_exhaustive(base: crate::any::TypeId, variant: usize) -> bool; + +/// Gets the number of fields at the given `variant_index` represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::fields`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_fields(_id: crate::any::TypeId, _variant_index: usize) -> usize; + +/// Checks whether this type is non-exhaustive. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_non_exhaustive(_id: crate::any::TypeId) -> bool; + +/// Returns the list of generic args on this type. +/// Only meaningful for Adts, closures, ... Everything else returns an empty slice. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_info::Generic]; + +/// Gets the [`FieldRepresentingType`]'s `TypeId` at the given index of the type represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::field`]. +/// +/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_field_representing_type( + _id: crate::any::TypeId, + _variant_index: usize, + _field_index: usize, +) -> crate::any::TypeId; + +/// Given a `TypeId` that represents a pointer this returns the `TypeId` which that pointer +/// points to. When called on anything else this returns None. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_to`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_points_to(_id: crate::any::TypeId) -> Option; + +/// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. +/// When called on anything else this returns `false`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_mutably`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_points_mutably(_id: crate::any::TypeId) -> bool; + +/// Given a `TypeId` that represents a function pointer returns an [`core::mem::type_info::FnPtr`]. +/// When called on something else this returns `None`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::function_ptr`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option; diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 1e96cf915e67d..71a9627c24f9c 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -2,10 +2,9 @@ //! runtime or const-eval processable way. use crate::any::TypeId; -use crate::fmt; -use crate::intrinsics; use crate::marker::PointeeSized; use crate::ptr::DynMetadata; +use crate::{fmt, intrinsics}; /// Compile-time type information. #[derive(Debug)] @@ -38,7 +37,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn info(self) -> Type { - intrinsics::type_id_type_of(self) + intrinsics::reflection::type_id_type_of(self) } } @@ -54,7 +53,7 @@ impl Type { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] pub const fn of() -> Self { - const { intrinsics::type_id::().info() } + const { intrinsics::reflection::type_id::().info() } } } @@ -67,7 +66,7 @@ impl Type { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] pub const fn of() -> TypeId { - const { intrinsics::type_id::() } + const { intrinsics::reflection::type_id::() } } /// Compile-time type information. @@ -272,7 +271,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn is_signed(self) -> bool { - intrinsics::type_id_is_signed(self) + intrinsics::reflection::type_id_is_signed(self) } /// When called on a `TypeId` representing an array or slice this returns the type of each @@ -291,7 +290,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn element_ty(self) -> Option { - intrinsics::type_id_element_ty(self) + intrinsics::reflection::type_id_element_ty(self) } /// When called on a `TypeId` representing an array this returns the length of the array in @@ -310,7 +309,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn array_len(self) -> usize { - intrinsics::type_id_array_len(self) + intrinsics::reflection::type_id_array_len(self) } /// Returns the size of the type represented by this `TypeId`. `None` if it is unsized. @@ -328,7 +327,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn size(self) -> Option { - intrinsics::type_id_size_of(self) + intrinsics::reflection::type_id_size_of(self) } /// Returns the number of variants of the type represented by this `TypeId`. @@ -354,7 +353,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn variants(self) -> usize { - intrinsics::type_id_variants(self) + intrinsics::reflection::type_id_variants(self) } // FIXME(reflection): make the errors nicer. This is a wider problem, @@ -416,7 +415,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn variant(self, variant_index: usize) -> VariantId { - intrinsics::type_id_fields(self, variant_index); + intrinsics::reflection::type_id_fields(self, variant_index); VariantId { base: self, variant: variant_index } } @@ -484,7 +483,7 @@ impl TypeId { // type we have) and add methods on that. It's the only way to really sensibly represent // things like `non_exhaustive` which can be applied to variants as well. pub fn fields(self, variant_index: usize) -> usize { - intrinsics::type_id_fields(self, variant_index) + intrinsics::reflection::type_id_fields(self, variant_index) } /// Returns the field representing type at the given index of the type represented by this `TypeId`. @@ -552,7 +551,7 @@ impl TypeId { #[rustc_comptime] pub fn field(self, variant_index: usize, field_index: usize) -> FieldId { FieldId { - frt_type_id: intrinsics::type_id_field_representing_type( + frt_type_id: intrinsics::reflection::type_id_field_representing_type( self, variant_index, field_index, @@ -566,7 +565,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn non_exhaustive(self) -> bool { - intrinsics::type_id_non_exhaustive(self) + intrinsics::reflection::type_id_non_exhaustive(self) } /// Returns a list of generic parameters of the type. @@ -575,7 +574,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn generics(self) -> &'static [Generic] { - intrinsics::type_id_generics(self) + intrinsics::reflection::type_id_generics(self) } /// Given a `TypeId` that represents a pointer this returns the `TypeId` @@ -600,7 +599,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn points_to(self) -> Option { - intrinsics::type_id_points_to(self) + intrinsics::reflection::type_id_points_to(self) } /// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. @@ -620,7 +619,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn points_mutably(self) -> bool { - intrinsics::type_id_points_mutably(self) + intrinsics::reflection::type_id_points_mutably(self) } /// Given a `TypeId` that represents a function pointer returns an @@ -655,7 +654,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn function_ptr(self) -> Option { - intrinsics::type_id_function_ptr(self) + intrinsics::reflection::type_id_function_ptr(self) } } @@ -696,7 +695,7 @@ impl VariantId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn name(self) -> &'static str { - intrinsics::type_id_variant_name(self.base, self.variant) + intrinsics::reflection::type_id_variant_name(self.base, self.variant) } /// Returns whether this variant is marked with `#[non_exhaustive]`. @@ -704,7 +703,7 @@ impl VariantId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn non_exhaustive(self) -> bool { - intrinsics::type_id_variant_non_exhaustive(self.base, self.variant) + intrinsics::reflection::type_id_variant_non_exhaustive(self.base, self.variant) } } diff --git a/library/core/src/unit.rs b/library/core/src/unit.rs index 5d9c79538e371..6c746c939b84a 100644 --- a/library/core/src/unit.rs +++ b/library/core/src/unit.rs @@ -1,4 +1,4 @@ -use crate::intrinsics::type_id; +use crate::intrinsics::reflection::type_id; /// Collapses all unit items from an iterator into one. /// diff --git a/library/coretests/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs index e562f49b0a2fc..0fec76d3bd65a 100644 --- a/library/coretests/tests/intrinsics.rs +++ b/library/coretests/tests/intrinsics.rs @@ -1,7 +1,7 @@ use core::any::TypeId; use core::intrinsics::assume; use std::fmt::Debug; -use std::intrinsics::type_id_vtable; +use std::intrinsics::reflection::type_id_vtable; use std::option::Option; use std::ptr::DynMetadata; diff --git a/tests/mir-opt/inline/type_id_eq.rs b/tests/mir-opt/inline/type_id_eq.rs index 72348447cdc45..0d4e2ca20cf0e 100644 --- a/tests/mir-opt/inline/type_id_eq.rs +++ b/tests/mir-opt/inline/type_id_eq.rs @@ -3,7 +3,7 @@ //@ compile-flags: --crate-type=lib -C panic=abort use std::any::{Any, TypeId}; -use std::intrinsics::type_id_eq; +use std::intrinsics::reflection::type_id_eq; struct A { a: i32, diff --git a/tests/ui-fulldeps/rustc_public/check_allocation.rs b/tests/ui-fulldeps/rustc_public/check_allocation.rs index 356ea64729cb7..cd52ff6dbeff4 100644 --- a/tests/ui-fulldeps/rustc_public/check_allocation.rs +++ b/tests/ui-fulldeps/rustc_public/check_allocation.rs @@ -250,7 +250,7 @@ fn generate_input(path: &str) -> std::io::Result<()> { r#" #![feature(core_intrinsics)] #![expect(internal_features)] - use std::intrinsics::type_id; + use std::intrinsics::reflection::type_id; static LEN: usize = 2; static FOO: [&str; 2] = ["hi", "there"];