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 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", diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 3f7731ce95583..55ddba1ec470f 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -608,7 +608,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)?; } @@ -627,7 +627,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() { @@ -764,7 +764,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() { @@ -790,7 +790,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() { @@ -890,7 +890,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 e360eaba99aae..281d7b0a921ba 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 @@ -126,7 +126,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_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_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index bcab1457e0655..714aaaf876c7d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -151,7 +151,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 @@ -208,17 +207,19 @@ 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_size_of + | 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::type_of | 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 @@ -297,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)), @@ -340,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); @@ -356,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()], @@ -365,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_public/src/rustc_internal/mod.rs b/compiler/rustc_public/src/rustc_internal/mod.rs index 49dc64572923a..d127a62862e78 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; @@ -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()) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1fe0fbdd2b6cd..781109d6c8658 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 { &[] 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); } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a003f20e3512d..21e250c140161 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1000,6 +1000,7 @@ symbols! { fields, file, final_associated_functions, + float_mul_add_relaxed, float_to_int_unchecked, floorf16, floorf32, @@ -2221,8 +2222,13 @@ 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_id_type_of, + type_id_variant_name, + type_id_variant_non_exhaustive, type_id_variants, type_id_vtable, type_info, @@ -2234,7 +2240,6 @@ symbols! { type_length_limit, type_macros, type_name, - type_of, type_privacy_lints, typed_swap_nonoverlapping, u8, @@ -2363,9 +2368,6 @@ symbols! { var, variadic, variant_count, - variant_name, - variant_non_exhaustive, - variants, vec, vector, verbatim, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 8f2d496f95b25..916a5682cb0c3 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, 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/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) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 8c049b118d703..c6cb5b0f50c56 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 f44da9840b8e5..1e8497b0ab439 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; @@ -1404,6 +1405,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 +1424,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 +1443,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 +1462,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] @@ -2986,25 +2999,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_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 @@ -3019,121 +3013,6 @@ pub fn 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 size_of_type_id(_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 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 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`]. @@ -3166,48 +3045,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 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 a11d1f1d0dc18..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::{self, type_id, type_of}; 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 { - 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 { 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::size_of_type_id(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::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::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::variant_non_exhaustive(self.base, self.variant) + intrinsics::reflection::type_id_variant_non_exhaustive(self.base, self.variant) } } diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index e53e4df1ea43d..2096b2f4d6d15 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 (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 NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f128)] @@ -1998,6 +1998,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 4ad41bd6cfa4b..7332c0a586b8f 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 (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 NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f16)] @@ -1984,6 +1984,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 2dc3ddf31cac5..c98bff20cdbf9 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 (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 NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0f32; @@ -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 cd3cfe1bd4c60..3a51e49f94f1d 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 (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 NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; @@ -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/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/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) })?; 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") } 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/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/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 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); }) } 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"]; 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; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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()); +}