diff --git a/Cargo.lock b/Cargo.lock index 90c8a59303c64..696b797e612f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -879,7 +879,6 @@ dependencies = [ "shim_utils", "tracing", "tracing-subscriber", - "unified-diff", "walkdir", "windows 0.61.3", ] @@ -6208,15 +6207,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "unified-diff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496a3d395ed0c30f411ceace4a91f7d93b148fb5a9b383d5d4cff7850f048d5f" -dependencies = [ - "diff", -] - [[package]] name = "unit-prefix" version = "0.5.2" diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index de563575f0171..d693061e8fa43 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -21,16 +21,19 @@ use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; -use rustc_infer::infer::NllRegionVariableOrigin; +use rustc_infer::infer::{NllRegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::traits::ObligationCause; use rustc_macros::extension; use rustc_middle::mir::RETURN_PLACE; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ self, BoundVariableKind, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, - List, RegionExt, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, + List, RegionExt, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, + fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, kw, sym}; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument}; use crate::BorrowckInferCtxt; @@ -133,12 +136,32 @@ pub(crate) enum DefiningTy<'tcx> { GlobalAsm(DefId), } +fn normalized_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { + let ty = tcx.type_of(def_id).instantiate_identity(); + if !tcx.next_trait_solver_globally() { + return ty.skip_normalization(); + } + + let typing_mode = if tcx.use_typing_mode_post_typeck_until_borrowck() { + TypingMode::borrowck(tcx, def_id) + } else { + TypingMode::analysis_in_body(tcx, def_id) + }; + let infcx = tcx.infer_ctxt().build(typing_mode); + let ocx = ObligationCtxt::new(&infcx); + let span = tcx.def_span(def_id); + let cause = ObligationCause::misc(span, def_id); + ocx.deeply_normalize(&cause, tcx.param_env(def_id), ty).unwrap() +} + impl<'tcx> DefiningTy<'tcx> { #[instrument(level = "debug", skip(tcx), ret)] pub(crate) fn new(tcx: TyCtxt<'tcx>, body_def_id: LocalDefId) -> DefiningTy<'tcx> { match tcx.hir_body_owner_kind(body_def_id) { BodyOwnerKind::Closure | BodyOwnerKind::Fn => { - let defining_ty = tcx.type_of(body_def_id).instantiate_identity().skip_norm_wip(); + // Normalize after instantiation so coroutine yield/resume + // types in the args are rigid under the next solver. + let defining_ty = normalized_type_of(tcx, body_def_id); match *defining_ty.kind() { ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args), ty::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, args), diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index d10b844bf3500..805c20755d9f7 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -455,8 +455,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // compile time. M::check_fn_target_features(self, instance)?; + // If the signature says this cannot unwind, reflect this in the unwind destination so that + // we don't have to check this later. (`init_fn_call` already did this for the caller so + // here we only have to check the callee.) if !callee_fn_abi.can_unwind { - // The callee cannot unwind, so force the `Unreachable` unwind handling. match &mut cont { ReturnContinuation::Stop { .. } => {} ReturnContinuation::Goto { unwind, .. } => { @@ -677,12 +679,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { with_caller_location: bool, destination: &PlaceTy<'tcx, M::Provenance>, target: Option, - unwind: mir::UnwindAction, + mut unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { let _trace = enter_trace_span!(M, step::init_fn_call, tracing_separate_thread = Empty, ?fn_val) .or_if_tracing_disabled(|| trace!("init_fn_call: {:#?}", fn_val)); + // If the signature says this cannot unwind, reflect this in the unwind destination + // so that we don't have to check this later. + if caller_fn_abi.is_some_and(|abi| !abi.can_unwind) { + unwind = mir::UnwindAction::Unreachable; + } + let instance = match fn_val { FnVal::Instance(instance) => instance, FnVal::Other(extra) => { diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index c70ceef1d47e9..7a69f58d52fae 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -58,12 +58,6 @@ declare_hooks! { /// Getting a &core::panic::Location referring to a span. hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue; - /// Returns `true` if this def is a function-like thing that is eligible for - /// coverage instrumentation under `-Cinstrument-coverage`. - /// - /// (Eligible functions might nevertheless be skipped for other reasons.) - hook is_eligible_for_coverage(key: LocalDefId) -> bool; - /// Imports all `SourceFile`s from the given crate into the current session. /// This normally happens automatically when we decode a `Span` from /// that crate's metadata - however, the incr comp cache needs diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index da02eb88d3875..dcfd7a6e610b8 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -722,6 +722,14 @@ rustc_queries! { separate_provide_extern } + /// Returns `true` if this def is a function-like thing that is eligible for + /// coverage instrumentation under `-Cinstrument-coverage`. + /// + /// (Eligible functions might nevertheless be skipped for other reasons.) + query is_eligible_for_coverage(key: LocalDefId) -> bool { + desc { "checking whether `{}` is eligible for coverage", tcx.def_path_str(key) } + } + /// Checks for the nearest `#[coverage(off)]` or `#[coverage(on)]` on /// this def and any enclosing defs, up to the crate root. /// diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 5efe62c3ebc21..223653232ba34 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -33,6 +33,7 @@ use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_ use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::traits::ObligationCause; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; use rustc_middle::mir::*; @@ -40,6 +41,7 @@ use rustc_middle::thir::{self, ExprId, LocalVarId, Param, ParamId, PatKind, Thir use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol}; +use rustc_trait_selection::traits::ObligationCtxt; use crate::builder::expr::as_place::PlaceBuilder; use crate::builder::scope::LintLevel; @@ -477,15 +479,6 @@ fn construct_fn<'tcx>( let arguments = &thir.params; let return_ty = fn_sig.output(); - let coroutine = match tcx.type_of(fn_def).instantiate_identity().skip_norm_wip().kind() { - ty::Coroutine(_, args) => Some(Box::new(CoroutineInfo::initial( - tcx.coroutine_kind(fn_def).unwrap(), - args.as_coroutine().yield_ty(), - args.as_coroutine().resume_ty(), - ))), - ty::Closure(..) | ty::CoroutineClosure(..) | ty::FnDef(..) => None, - ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"), - }; if let Some((dialect, phase)) = find_attr!(tcx, fn_id, CustomMir(dialect, phase) => (dialect, phase)) @@ -514,6 +507,17 @@ fn construct_fn<'tcx>( }; let infcx = tcx.infer_ctxt().build(typing_mode); + + let coroutine = match normalized_type_of(&infcx, fn_def).kind() { + ty::Coroutine(_, args) => Some(Box::new(CoroutineInfo::initial( + tcx.coroutine_kind(fn_def).unwrap(), + args.as_coroutine().yield_ty(), + args.as_coroutine().resume_ty(), + ))), + ty::Closure(..) | ty::CoroutineClosure(..) | ty::FnDef(..) => None, + ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"), + }; + let mut builder = Builder::new( thir, infcx, @@ -562,6 +566,18 @@ fn construct_fn<'tcx>( body } +fn normalized_type_of<'tcx>(infcx: &InferCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { + let tcx = infcx.tcx; + let ty = tcx.type_of(def_id).instantiate_identity(); + if !infcx.next_trait_solver() { + return ty.skip_normalization(); + } + + let ocx = ObligationCtxt::new(infcx); + let cause = ObligationCause::misc(tcx.def_span(def_id), def_id); + ocx.deeply_normalize(&cause, tcx.param_env(def_id), ty).unwrap() +} + fn construct_const<'a, 'tcx>( tcx: TyCtxt<'tcx>, def: LocalDefId, diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 39e0769373ede..d0fc31bfa7f70 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -14,12 +14,12 @@ use crate::coverage::counters::{CoverageCounters, transcribe_counters}; /// Registers query/hook implementations related to coverage. pub(crate) fn provide(providers: &mut Providers) { - providers.hooks.is_eligible_for_coverage = is_eligible_for_coverage; + providers.queries.is_eligible_for_coverage = is_eligible_for_coverage; providers.queries.coverage_attr_on = coverage_attr_on; providers.queries.coverage_ids_info = coverage_ids_info; } -/// Hook implementation for [`TyCtxt::is_eligible_for_coverage`]. +/// Query implementation for [`TyCtxt::is_eligible_for_coverage`]. fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Only instrument functions, methods, and closures (not constants since they are evaluated // at compile time by Miri). diff --git a/compiler/rustc_thread_pool/Cargo.toml b/compiler/rustc_thread_pool/Cargo.toml index c92984470b7ae..c7d04da428b0d 100644 --- a/compiler/rustc_thread_pool/Cargo.toml +++ b/compiler/rustc_thread_pool/Cargo.toml @@ -8,7 +8,6 @@ authors = [ description = "Core APIs for Rayon - fork for rustc" license = "MIT OR Apache-2.0" edition = "2021" -readme = "README.md" keywords = ["parallel", "thread", "concurrency", "join", "performance"] categories = ["concurrency"] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index bd15ec798460f..7965a10c98289 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -170,6 +170,9 @@ use self::spec_extend::SpecExtend; #[cfg(not(no_global_oom_handling))] mod spec_extend; +#[cfg(all(target_arch = "aarch64", target_feature = "sve"))] +mod sve_retain; + /// A contiguous growable array type, written as `Vec`, short for 'vector'. /// /// # Examples @@ -2514,6 +2517,22 @@ impl Vec { return; } + #[cfg(all(target_arch = "aarch64", target_feature = "sve"))] + { + let long_enough = match mem::size_of::() { + 1 => original_len >= sve_retain::MIN_SVE_SIZE_1, + 2 => original_len >= sve_retain::MIN_SVE_SIZE_2, + 4 => original_len >= sve_retain::MIN_SVE_SIZE_4, + 8 => original_len >= sve_retain::MIN_SVE_SIZE_8, + _ => false, + }; + if long_enough { + // SAFETY: size_of::() is 1, 2, 4 or 8, matching + // the kernel lane widths. + return unsafe { sve_retain::chunked_retain(self, f) }; + } + } + // Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked] // | ^- write ^- read | // |<- original_len ->| diff --git a/library/alloc/src/vec/sve_retain.rs b/library/alloc/src/vec/sve_retain.rs new file mode 100644 index 0000000000000..31e0e79888744 --- /dev/null +++ b/library/alloc/src/vec/sve_retain.rs @@ -0,0 +1,251 @@ +//! SVE-accelerated `Vec::retain_mut` implementation. +//! +//! Two-phase algorithm per 64-element chunk: +//! - Phase A (scalar): evaluates predicate exactly once, in order, into a bool mask. +//! - Phase B (SVE): uses `COMPACT` instruction to compress retained elements. + +use core::{cmp, mem, ptr}; + +use super::Vec; +use crate::alloc::Allocator; + +const CHUNK_SIZE: usize = 64; + +pub(super) const MIN_SVE_SIZE_1: usize = 32; +pub(super) const MIN_SVE_SIZE_2: usize = 32; +pub(super) const MIN_SVE_SIZE_4: usize = 64; +pub(super) const MIN_SVE_SIZE_8: usize = 64; + +/// Guard for the SVE retain path. On panic, this guard: +/// 1. Scalar-compresses the already-decided prefix of the current chunk. +/// 2. Copies the untouched tail forward. +/// 3. Sets the Vec length. +struct PanicGuard<'a, T, A: Allocator> { + v: &'a mut Vec, + /// Start index of the current chunk within the Vec. + read: usize, + /// Write cursor (accumulated from previous chunks). + write: usize, + /// How many elements in the current chunk have been decided by the predicate. + decided: usize, + + mask: &'a mut [bool; CHUNK_SIZE], + /// Original length of the Vec. + original_len: usize, +} + +impl Drop for PanicGuard<'_, T, A> { + #[cold] + fn drop(&mut self) { + // Scalar-compress the decided prefix: move kept elements to write position. + let mut dst = self.write; + for i in 0..self.decided { + if self.mask[i] { + // SAFETY: read + i < original_len (in-bounds). + let src = unsafe { self.v.as_ptr().add(self.read + i) }; + let dst_ptr = unsafe { self.v.as_mut_ptr().add(dst) }; + // SAFETY: src and dst_ptr < original_len + unsafe { ptr::copy(src, dst_ptr, 1) }; + dst += 1; + } + } + + // Copy the untouched tail. + let untouched_start = self.read + self.decided; + let untouched_len = self.original_len - untouched_start; + if untouched_len > 0 { + // SAFETY: untouched_start..original_len are valid; dst + untouched_len <= original_len. + unsafe { + ptr::copy( + self.v.as_ptr().add(untouched_start), + self.v.as_mut_ptr().add(dst), + untouched_len, + ); + } + dst += untouched_len; + } + + // SAFETY: After filling holes, all items are in contiguous memory. + unsafe { self.v.set_len(dst) }; + } +} + +/// # Safety +/// +/// - `size_of::() == 1/2/4/8` +/// - Called on aarch64 + SVE target. +pub(crate) unsafe fn chunked_retain(v: &mut Vec, mut f: F) +where + F: FnMut(&mut T) -> bool, +{ + let original_len = v.len(); + let mut guard = PanicGuard { + v, + read: 0, + write: 0, + decided: 0, + mask: &mut [false; CHUNK_SIZE], + original_len, + }; + + while guard.read < guard.original_len { + let chunk_len = cmp::min(CHUNK_SIZE, guard.original_len - guard.read); + + guard.decided = 0; + + // Phase A: scalar predicate evaluation (exactly once, in order). + for i in 0..chunk_len { + // SAFETY: read + i < original_len + let cur = unsafe { &mut *guard.v.as_mut_ptr().add(guard.read + i) }; + guard.mask[i] = f(cur); + guard.decided = i + 1; + if !guard.mask[i] { + // SAFETY: read + i < original_len, and after marking `mask` and `decided`, + // the guard can properly handles the case where drop_in_place panics. + unsafe { ptr::drop_in_place(cur) }; + } + } + + // Phase B: SVE compress. + // SAFETY: write <= read and the dispatch guarantees size_of::() matches the kernel lane width. + let kept = match mem::size_of::() { + 1 => unsafe { + compact8_kernel( + guard.v.as_mut_ptr().add(guard.read), + guard.v.as_mut_ptr().add(guard.write), + guard.mask.as_ptr(), + chunk_len, + ) + }, + 2 => unsafe { + compact16_kernel( + guard.v.as_mut_ptr().add(guard.read), + guard.v.as_mut_ptr().add(guard.write), + guard.mask.as_ptr(), + chunk_len, + ) + }, + 4 => unsafe { + compact32_kernel( + guard.v.as_mut_ptr().add(guard.read), + guard.v.as_mut_ptr().add(guard.write), + guard.mask.as_ptr(), + chunk_len, + ) + }, + 8 => unsafe { + compact64_kernel( + guard.v.as_mut_ptr().add(guard.read), + guard.v.as_mut_ptr().add(guard.write), + guard.mask.as_ptr(), + chunk_len, + ) + }, + _ => unreachable!(), + }; + + guard.write += kept; + guard.read += chunk_len; + } + + // SAFETY: write <= original_len, all retained elements are packed at the front. + unsafe { guard.v.set_len(guard.write) }; + mem::forget(guard); +} + +macro_rules! sve_compact_kernel { + ( + $name:ident, + size = $size:literal, + lane = $lane:literal, + mem_lane = $mem_lane:literal, + shift = $shift:literal, + inc = $inc:literal + ) => { + /// SVE compress kernel: pack retained elements from `src` to `dst` + /// according to `mask`. Returns the number of retained elements. + /// + /// # Safety + /// + /// - `src` is valid for `chunk_len` reads of `T`, `dst` for `chunk_len` + /// writes, `mask` for `chunk_len` bool reads. + /// - `size_of::()` equals this kernel's lane width in bytes. + #[target_feature(enable = "sve")] + #[inline] + unsafe fn $name( + src: *const T, + dst: *mut T, + mask: *const bool, + chunk_len: usize, + ) -> usize { + debug_assert_eq!(mem::size_of::(), $size); + + let idx_in = 0usize; + let mut idx_out = 0usize; + // SVE intrinsics require treating the data as integers, which doesn't + // correctly handle provenance and uninitialized padding. + // + // SAFETY: whilelo predicates every load/store to the remaining elements. + unsafe { + core::arch::asm!( + concat!("whilelo p0.", $lane, ", xzr, {len}"), + "2:", + concat!("ld1b {{ z0.", $lane, " }}, p0/z, [{mask}, {idx_in}]"), + concat!("cmpne p1.", $lane, ", p0/z, z0.", $lane, ", #0"), + concat!("ld1", $mem_lane, " {{ z1.", $lane, " }}, p1/z, [{src}, {idx_in}", $shift, "]"), + concat!("compact z1.", $lane, ", p1, z1.", $lane), + concat!("cntp {kept}, p0, p1.", $lane), + concat!("whilelo p2.", $lane, ", xzr, {kept}"), + concat!("st1", $mem_lane, " {{ z1.", $lane, " }}, p2, [{dst}, {idx_out}", $shift, "]"), + concat!("add {idx_out}, {idx_out}, {kept}"), + concat!("inc", $inc, " {idx_in}"), + concat!("whilelo p0.", $lane, ", {idx_in}, {len}"), + "b.first 2b", + src = in(reg) src, + dst = in(reg) dst, + mask = in(reg) mask, + len = in(reg) chunk_len, + idx_in = inout(reg) idx_in => _, + idx_out = inout(reg) idx_out, + kept = out(reg) _, + out("p0") _, + out("p1") _, + out("p2") _, + out("z0") _, + out("z1") _, + options(nostack), + ); + } + idx_out + } + }; +} + +sve_compact_kernel!(compact8_kernel, size = 1, lane = "s", mem_lane = "b", shift = "", inc = "w"); + +sve_compact_kernel!( + compact16_kernel, + size = 2, + lane = "s", + mem_lane = "h", + shift = ", lsl #1", + inc = "w" +); + +sve_compact_kernel!( + compact32_kernel, + size = 4, + lane = "s", + mem_lane = "w", + shift = ", lsl #2", + inc = "w" +); + +sve_compact_kernel!( + compact64_kernel, + size = 8, + lane = "d", + mem_lane = "d", + shift = ", lsl #3", + inc = "d" +); diff --git a/library/alloctests/benches/lib.rs b/library/alloctests/benches/lib.rs index 974b389a765d5..336e83659f3cb 100644 --- a/library/alloctests/benches/lib.rs +++ b/library/alloctests/benches/lib.rs @@ -2,6 +2,7 @@ #![cfg(not(miri))] #![allow(internal_features)] #![feature(iter_next_chunk)] +#![feature(macro_metavar_expr_concat)] #![feature(repr_simd)] #![feature(slice_partition_dedup)] #![feature(strict_provenance_lints)] diff --git a/library/alloctests/benches/vec.rs b/library/alloctests/benches/vec.rs index 1dab71fa1f4f4..656164da72084 100644 --- a/library/alloctests/benches/vec.rs +++ b/library/alloctests/benches/vec.rs @@ -857,6 +857,69 @@ fn bench_retain_whole_100000(b: &mut Bencher) { b.iter(|| v.retain(|x| *x == 826u32)); } +macro_rules! retain_type_benches { + ( + len = $len:literal, + suffix = $suffix:literal, + types = [$($ty:ident),*] + ) => { + $( + #[bench] + fn ${concat(bench_retain_, $ty, _, $suffix)}(b: &mut Bencher) { + let mut v: Vec<$ty> = Vec::with_capacity($len); + b.iter(|| { + v.clear(); + v.extend(black_box((0..$len).map(|x| { x as $ty }))); + v.retain(|x| *x & 1 == 0) + }); + } + + #[bench] + fn ${concat(bench_retain_whole_, $ty, _, $suffix)}(b: &mut Bencher) { + let mut v = black_box(vec![82 as $ty; $len]); + b.iter(|| v.retain(|x| *x == 82 )); + } + )* + }; +} + +macro_rules! retain_matrix_benches { + ($($len:literal => $suffix:literal;)*) => { + $( + retain_type_benches! { + len = $len, + suffix = $suffix, + types = [u8, u16, u32, u64] + } + + #[bench] + fn ${concat(bench_retain_iter_u32_, $suffix)}(b: &mut Bencher) { + let mut v: Vec = Vec::with_capacity($len); + b.iter(|| { + let mut tmp = std::mem::take(&mut v); + tmp.clear(); + tmp.extend(black_box(1..=$len as u32)); + v = tmp.into_iter().filter(|x| x & 1 == 0).collect(); + }); + } + )* + }; +} + +retain_matrix_benches! { + 4 => "000004"; + 8 => "000008"; + 16 => "000016"; + 32 => "000032"; + 64 => "000064"; + 128 => "000128"; + 256 => "000256"; + 512 => "000512"; + 1000 => "001000"; + 10000 => "010000"; + 100000 => "100000"; +} + #[bench] fn bench_next_chunk(b: &mut Bencher) { let v = vec![13u8; 2048]; diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 45d84d533fe14..36254c8280180 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -251,6 +251,7 @@ impl Step for PrepareRustcRmetaSysroot { // Copy the generated rmeta artifacts to a separate directory let dir = builder + .config .out .join(build_compiler.host) .join(format!("stage{}-rustc-rmeta-artifacts", build_compiler.stage + 1)); @@ -289,6 +290,7 @@ impl Step for PrepareStdRmetaSysroot { // Copy the generated rmeta artifacts to a separate directory let dir = builder + .config .out .join(self.build_compiler.host) .join(format!("stage{}-std-rmeta-artifacts", self.build_compiler.stage)); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index a1fdffc35f226..afdda908a2578 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -778,7 +778,6 @@ impl Step for StdLink { let is_downloaded_beta_stage0 = builder .sess - .config .initial_rustc .starts_with(builder.out.join(compiler.host).join("stage0/bin")); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 713d9a6426fc7..01c87491c8ad9 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1585,7 +1585,7 @@ impl CommandLineStep for RustdocGUI { cmd.arg("--out-dir").arg(out_dir); } - if let Some(initial_cargo) = builder.config.initial_cargo.to_str() { + if let Some(initial_cargo) = builder.sess.initial_cargo.to_str() { cmd.arg("--initial-cargo").arg(initial_cargo); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 922784fb7e13a..c4fae69645e85 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -797,7 +797,7 @@ impl CommandLineStep for Rustdoc { // Cargo adds a number of paths to the dylib search path on windows, which results in // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" // rustdoc a different name. - tool: "rustdoc_tool_binary", + tool: "rustdoc-tool-binary", mode: Mode::ToolRustcPrivate, path: "src/tools/rustdoc", source_type: SourceType::InTree, diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 0d7f4613723dc..64759e7c53f20 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1462,11 +1462,10 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// The used Clippy is (or in the case of stage 0, already was) built using `build_compiler`. pub fn cargo_clippy_cmd(&self, build_compiler: Compiler) -> BootstrapCommand { if build_compiler.stage == 0 { - let cargo_clippy = self - .config - .initial_cargo_clippy - .clone() - .unwrap_or_else(|| self.sess.config.download_clippy()); + let cargo_clippy = + self.config.external_cargo_clippy.clone().unwrap_or_else(|| { + self.sess.config.download_clippy(&self.sess.initial_sysroot) + }); let mut cmd = command(cargo_clippy); cmd.env("CARGO", &self.initial_cargo); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 6df39a0927738..f4c96defa7ae6 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -53,7 +53,7 @@ use crate::core::config::{ GccCiMode, LlvmCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, TargetSelection, threads_from_config, }; -use crate::core::download::{DownloadContext, download_beta_toolchain, is_download_ci_available}; +use crate::core::download::{DownloadContext, is_download_ci_available}; use crate::utils::channel::{self, GitInfo}; use crate::utils::exec::{ExecutionContext, command}; use crate::utils::helpers::{self, exe, fail, get_host_target, t}; @@ -243,9 +243,13 @@ pub(crate) struct Config { pub reproducible_artifacts: Vec, + /// Build triple for the pre-compiled snapshot compiler. pub host_target: TargetSelection, + /// Which triples to produce a compiler toolchain for. pub hosts: Vec, + /// Which triples to build libraries (core/alloc/std/test/proc_macro) for. pub targets: Vec, + pub local_rebuild: bool, pub allocator: Option, pub control_flow_guard: bool, @@ -301,12 +305,13 @@ pub(crate) struct Config { pub in_tree_llvm_info: channel::GitInfo, pub in_tree_gcc_info: channel::GitInfo, - // These are either the stage0 downloaded binaries or the locally installed ones. - pub initial_cargo: PathBuf, - pub initial_rustc: PathBuf, - pub initial_rustdoc: PathBuf, - pub initial_cargo_clippy: Option, - pub initial_sysroot: PathBuf, + /// rustc/cargo/rustdoc/clippy paths specified in the config file + /// Access the `initial_` fields from `Session` to use either the externally configured + /// or downloaded (stage0) binaries. + pub external_cargo: Option, + pub external_rustc: Option, + pub external_rustdoc: Option, + pub external_cargo_clippy: Option, /// Externally configured `rustfmt` binary for formatting in-tree source code. /// If you want to use rustfmt for formatting, use the `InternalRustfmt` step, instead of @@ -499,7 +504,6 @@ impl Config { gdb: build_gdb, lldb: build_lldb, nodejs: build_nodejs, - yarn: build_yarn, npm: build_npm, python: build_python, @@ -771,7 +775,7 @@ impl Config { // NOTE: Bootstrap spawns various commands with different working directories. // To avoid writing to random places on the file system, `config.out` needs to be an absolute path. - let mut out = if !out.is_absolute() { + let out = if !out.is_absolute() { // `canonicalize` requires the path to already exist. Use our vendored copy of `absolute` instead. absolute(&out).expect("can't make empty path absolute") } else { @@ -810,10 +814,10 @@ impl Config { if !flags_skip_stage0_validation { if let Some(rustc) = &build_rustc { - check_stage0_version(rustc, "rustc", &src, &exec_ctx); + check_external_binary_version(rustc, "rustc", &src, &exec_ctx); } if let Some(cargo) = &build_cargo { - check_stage0_version(cargo, "cargo", &src, &exec_ctx); + check_external_binary_version(cargo, "cargo", &src, &exec_ctx); } } @@ -841,34 +845,6 @@ impl Config { ci_env, }; - let initial_rustc = build_rustc.unwrap_or_else(|| { - download_beta_toolchain(&dwn_ctx, &out); - default_stage0_rustc_path(&out) - }); - - let initial_rustdoc = build_rustdoc - .unwrap_or_else(|| initial_rustc.with_file_name(exe("rustdoc", host_target))); - - let initial_sysroot = t!(PathBuf::from_str( - command(&initial_rustc) - .args(["--print", "sysroot"]) - .run_in_dry_run() - .run_capture_stdout(&exec_ctx) - .stdout() - .trim() - )); - - let initial_cargo = build_cargo.unwrap_or_else(|| { - download_beta_toolchain(&dwn_ctx, &out); - initial_sysroot.join("bin").join(exe("cargo", host_target)) - }); - - // NOTE: it's important this comes *after* we set `initial_rustc` just above. - if exec_ctx.dry_run() { - out = out.join("tmp-dry-run"); - fs::create_dir_all(&out).expect("Failed to create dry-run directory"); - } - let file_content = t!(fs::read_to_string(src.join("src/ci/channel"))); let ci_channel = file_content.trim_end(); @@ -1464,6 +1440,10 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to explicit_stage_from_cli: flags_stage.is_some(), explicit_stage_from_config, extended: build_extended.unwrap_or(false), + external_cargo: build_cargo, + external_cargo_clippy: build_cargo_clippy, + external_rustc: build_rustc, + external_rustdoc: build_rustdoc, external_rustfmt: build_rustfmt, free_args: flags_free_args, full_bootstrap: build_full_bootstrap.unwrap_or(false), @@ -1475,11 +1455,6 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to in_tree_llvm_info, include_default_paths: flags_include_default_paths, incremental: flags_incremental || rust_incremental == Some(true), - initial_cargo, - initial_cargo_clippy: build_cargo_clippy, - initial_rustc, - initial_rustdoc, - initial_sysroot, jobs: Some(threads_from_config(flags_jobs.or(build_jobs).unwrap_or(0))), json_output: flags_json_output, keep_stage: flags_keep_stage, @@ -2275,8 +2250,9 @@ fn postprocess_toml( toml.merge(None, &mut Default::default(), override_toml, ReplaceOpt::Override); } -/// check rustc/cargo version is same or lower with 1 apart from the building one -pub fn check_stage0_version( +/// Check that the version of an externally provided rustc/cargo is either the same or 1 version +/// older than the in-tree version. +fn check_external_binary_version( program_path: &Path, component_name: &'static str, src_dir: &Path, @@ -2286,32 +2262,30 @@ pub fn check_stage0_version( return; } - let stage0_output = - command(program_path).arg("--version").run_capture_stdout(exec_ctx).stdout(); - let mut stage0_output = stage0_output.lines().next().unwrap().split(' '); + let output = command(program_path).arg("--version").run_capture_stdout(exec_ctx).stdout(); + let mut output = output.lines().next().unwrap().split(' '); - let stage0_name = stage0_output.next().unwrap(); - if stage0_name != component_name { + let name = output.next().unwrap(); + if name != component_name { fail(&format!( - "Expected to find {component_name} at {} but it claims to be {stage0_name}", + "Expected to find {component_name} at {} but it claims to be {name}", program_path.display() )); } - let stage0_version = - semver::Version::parse(stage0_output.next().unwrap().split('-').next().unwrap().trim()) - .unwrap(); + let binary_version = + semver::Version::parse(output.next().unwrap().split('-').next().unwrap().trim()).unwrap(); let source_version = semver::Version::parse(fs::read_to_string(src_dir.join("src/version")).unwrap().trim()) .unwrap(); - if !(source_version == stage0_version - || (source_version.major == stage0_version.major - && (source_version.minor == stage0_version.minor - || source_version.minor == stage0_version.minor + 1))) + if !(source_version == binary_version + || (source_version.major == binary_version.major + && (source_version.minor == binary_version.minor + || source_version.minor == binary_version.minor + 1))) { let prev_version = format!("{}.{}.x", source_version.major, source_version.minor - 1); fail(&format!( - "Unexpected {component_name} version: {stage0_version}, we should use {prev_version}/{source_version} to build source with {source_version}" + "Unexpected {component_name} version: {binary_version}, we should use {prev_version}/{source_version} to build source with {source_version}" )); } } diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 29f0810b972bb..9a3cfe4a791d4 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -108,16 +108,15 @@ enum DownloadSource { /// Functions that are only ever called once, but named for clarity and to avoid thousand-line functions. impl Config { - pub(crate) fn download_clippy(&self) -> PathBuf { + pub(crate) fn download_clippy(&self, initial_sysroot: &Path) -> PathBuf { self.do_if_verbose(|| println!("downloading stage0 clippy artifacts")); let date = &self.stage0_metadata.compiler.date; let version = &self.stage0_metadata.compiler.version; let host = self.host_target; - let clippy_stamp = - BuildStamp::new(&self.initial_sysroot).with_prefix("clippy").add_stamp(date); - let cargo_clippy = self.initial_sysroot.join("bin").join(exe("cargo-clippy", host)); + let clippy_stamp = BuildStamp::new(initial_sysroot).with_prefix("clippy").add_stamp(date); + let cargo_clippy = initial_sysroot.join("bin").join(exe("cargo-clippy", host)); if cargo_clippy.exists() && clippy_stamp.is_up_to_date() { return cargo_clippy; } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 27638810fb7bb..5ce92f273695b 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -201,7 +201,7 @@ than building it. .map(|p| cmd_finder.must_have(p)) .or_else(|| cmd_finder.maybe_have("reuse")); - let stage0_supported_target_list: HashSet = command(&sess.config.initial_rustc) + let stage0_supported_target_list: HashSet = command(&sess.initial_rustc) .args(["--print", "target-list"]) .run_in_dry_run() .run_capture_stdout(&sess) @@ -314,7 +314,7 @@ than building it. } } - for target in &sess.targets { + for target in &sess.config.targets { sess.config .target_config .entry(*target) diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index d830fa2086386..db61dda61c7c7 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -1,8 +1,8 @@ use std::cell::Cell; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Display; +use std::ops::Deref; use std::path::{Path, PathBuf}; -use std::sync::OnceLock; use std::time::{Instant, SystemTime}; use std::{env, fs, io, str}; @@ -18,6 +18,7 @@ use crate::core::builder::{Builder, Kind}; use crate::core::compiler::Compiler; use crate::core::config::flags::{self, Subcommand}; use crate::core::config::{BootstrapOverrideLld, Config, DryRun, LlvmLibunwind, TargetSelection}; +use crate::core::download::{DownloadContext, download_beta_toolchain}; use crate::core::metadata::Crate; #[cfg(feature = "tracing")] use crate::trace_io; @@ -48,29 +49,11 @@ pub(crate) struct Session { pub(crate) version: String, // Properties derived from the above configuration - pub(crate) src: PathBuf, - pub(crate) out: PathBuf, pub(crate) bootstrap_out: PathBuf, - pub(crate) cargo_info: GitInfo, - pub(crate) rust_analyzer_info: GitInfo, - pub(crate) clippy_info: GitInfo, - pub(crate) miri_info: GitInfo, - pub(crate) rustfmt_info: GitInfo, - pub(crate) enzyme_info: GitInfo, - pub(crate) in_tree_llvm_info: GitInfo, - pub(crate) in_tree_gcc_info: GitInfo, - pub(crate) local_rebuild: bool, pub(crate) fail_fast: bool, pub(crate) test_target: TestTarget, pub(crate) verbosity: usize, - /// Build triple for the pre-compiled snapshot compiler. - pub(crate) host_target: TargetSelection, - /// Which triples to produce a compiler toolchain for. - pub(crate) hosts: Vec, - /// Which triples to build libraries (core/alloc/std/test/proc_macro) for. - pub(crate) targets: Vec, - pub(crate) initial_rustc: PathBuf, pub(crate) initial_rustdoc: PathBuf, pub(crate) initial_cargo: PathBuf, @@ -100,6 +83,14 @@ pub(crate) struct Session { pub(crate) step_graph: std::cell::RefCell, } +impl Deref for Session { + type Target = Config; + + fn deref(&self) -> &Self::Target { + &self.config + } +} + /// When building Rust various objects are handled differently. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub(crate) enum DependencyType { @@ -268,9 +259,6 @@ impl Session { /// /// By default all build output will be placed in the current directory. pub(crate) fn new(mut config: Config) -> Session { - let src = config.src.clone(); - let out = config.out.clone(); - #[cfg(unix)] // keep this consistent with the equivalent check in x.py: // https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797 @@ -288,27 +276,54 @@ impl Session { #[cfg(not(unix))] let is_sudo = false; - let rust_info = config.rust_info.clone(); - let cargo_info = config.cargo_info.clone(); - let rust_analyzer_info = config.rust_analyzer_info.clone(); - let clippy_info = config.clippy_info.clone(); - let miri_info = config.miri_info.clone(); - let rustfmt_info = config.rustfmt_info.clone(); - let enzyme_info = config.enzyme_info.clone(); - let in_tree_llvm_info = config.in_tree_llvm_info.clone(); - let in_tree_gcc_info = config.in_tree_gcc_info.clone(); - - let initial_target_libdir = command(&config.initial_rustc) + let dwn_ctx = DownloadContext::from(&config); + + let initial_rustc = config.external_rustc.clone().unwrap_or_else(|| { + download_beta_toolchain(&dwn_ctx, &config.out); + config + .out + .join(config.host_target) + .join("stage0") + .join("bin") + .join(exe("rustc", config.host_target)) + }); + + let initial_rustdoc = config + .external_rustdoc + .clone() + .unwrap_or_else(|| initial_rustc.with_file_name(exe("rustdoc", config.host_target))); + + // Gather both the sysroot and the target libdir to avoid an unnecessary rustc execution + // and speed up bootstrap slightly. + let rustc_paths = command(&initial_rustc) + .args(["--print", "sysroot", "--print", "target-libdir"]) .run_in_dry_run() - .args(["--print", "target-libdir"]) .run_capture_stdout(&config) - .stdout() - .trim() - .to_owned(); + .stdout(); + let mut rustc_paths = rustc_paths.lines(); + let initial_sysroot = + rustc_paths.next().map(PathBuf::from).expect("Missing sysroot from initial rustc"); + let initial_target_libdir = rustc_paths + .next() + .map(PathBuf::from) + .expect("Missing target libdir from initial rustc"); + assert!(rustc_paths.next().is_none()); + + let initial_cargo = config.external_cargo.clone().unwrap_or_else(|| { + download_beta_toolchain(&dwn_ctx, &config.out); + initial_sysroot.join("bin").join(exe("cargo", config.host_target)) + }); + + // NOTE: it's important this comes *after* we potentially download the binaries above, + // in order to not redownload them into a temporary directory. + if config.exec_ctx.dry_run() { + config.out = config.out.join("tmp-dry-run"); + fs::create_dir_all(&config.out).expect("Failed to create dry-run directory"); + } - let initial_target_dir = Path::new(&initial_target_libdir) + let initial_target_dir = initial_target_libdir .parent() - .unwrap_or_else(|| panic!("{initial_target_libdir} has no parent")); + .unwrap_or_else(|| panic!("{initial_target_libdir:?} has no parent")); let initial_lld = initial_target_dir.join("bin").join("rust-lld"); @@ -321,7 +336,7 @@ impl Session { }); ancestor - .strip_prefix(&config.initial_sysroot) + .strip_prefix(&initial_sysroot) .unwrap_or_else(|_| { panic!( "Couldn’t resolve the initial relative libdir from {}", @@ -331,7 +346,7 @@ impl Session { .to_path_buf() }; - let version = std::fs::read_to_string(src.join("src").join("version")) + let version = std::fs::read_to_string(config.src.join("src").join("version")) .expect("failed to read src/version"); let version = version.trim(); @@ -353,40 +368,24 @@ impl Session { ) } - if rust_info.is_from_tarball() && config.description.is_none() { + if config.rust_info.is_from_tarball() && config.description.is_none() { config.description = Some("built from a source tarball".to_owned()); } let mut sess = Session { initial_lld, initial_relative_libdir, - initial_rustc: config.initial_rustc.clone(), - initial_rustdoc: config.initial_rustdoc.clone(), - initial_cargo: config.initial_cargo.clone(), - initial_sysroot: config.initial_sysroot.clone(), - local_rebuild: config.local_rebuild, + initial_rustc, + initial_rustdoc, + initial_cargo, + initial_sysroot, fail_fast: config.cmd.fail_fast(), test_target: config.cmd.test_target(), verbosity: config.exec_ctx.verbosity as usize, - - host_target: config.host_target, - hosts: config.hosts.clone(), - targets: config.targets.clone(), - config, version: version.to_string(), - src, - out, bootstrap_out, - cargo_info, - rust_analyzer_info, - clippy_info, - miri_info, - rustfmt_info, - enzyme_info, - in_tree_llvm_info, - in_tree_gcc_info, cc: HashMap::new(), cxx: HashMap::new(), ar: HashMap::new(), @@ -419,7 +418,7 @@ impl Session { .trim(); if local_release.split('.').take(2).eq(version.split('.').take(2)) { sess.do_if_verbose(|| println!("auto-detected local-rebuild {local_release}")); - sess.local_rebuild = true; + sess.config.local_rebuild = true; } sess.do_if_verbose(|| println!("finding compilers")); @@ -841,17 +840,7 @@ impl Session { /// Returns the sysroot of the snapshot compiler. pub(crate) fn rustc_snapshot_sysroot(&self) -> &Path { - static SYSROOT_CACHE: OnceLock = OnceLock::new(); - SYSROOT_CACHE.get_or_init(|| { - command(&self.initial_rustc) - .run_in_dry_run() - .args(["--print", "sysroot"]) - .run_capture_stdout(self) - .stdout() - .trim() - .to_owned() - .into() - }) + &self.initial_sysroot } pub(crate) fn info(&self, msg: &str) { diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index 861c9953b52d1..b31850759687a 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -151,8 +151,8 @@ fn test_find() { let mut sess = Session::new(config); let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let target2 = TargetSelection::from_user("x86_64-unknown-openbsd"); - sess.targets.push(target1.clone()); - sess.hosts.push(target2.clone()); + sess.config.targets.push(target1.clone()); + sess.config.hosts.push(target2.clone()); fill_compilers(&mut sess); for t in sess.hosts.iter().chain(sess.targets.iter()).chain(iter::once(&sess.host_target)) { assert!(sess.cc.contains_key(t), "CC not set for target {}", t.triple); diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 09c19be3dafd6..6f8c8eed07d18 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -34,7 +34,6 @@ serde_json = "1.0" shim_utils = { path = "../../shim_utils" } tracing = "0.1" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["ansi", "env-filter", "fmt", "parking_lot", "smallvec"] } -unified-diff = "0.2.1" walkdir = "2" # tidy-alphabetical-end diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 135a2914c8fc8..7d25163bca065 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -124,7 +124,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match method { SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => { let [size, align] = this.check_shim_sig( - shim_sig_nounwind!(extern "Rust" fn(usize, core::mem::Alignment) -> *_), + shim_sig!(extern "Rust" fn(usize, core::mem::Alignment) -> *_), (link_name, abi, args), )?; let size = this.read_target_usize(size)?; @@ -147,7 +147,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } SpecialAllocatorMethod::Dealloc => { let [ptr, old_size, align] = this.check_shim_sig( - shim_sig_nounwind!(extern "Rust" fn(*_, usize, core::mem::Alignment) -> ()), + shim_sig!(extern "Rust" fn(*_, usize, core::mem::Alignment) -> ()), (link_name, abi, args), )?; let ptr = this.read_pointer(ptr)?; @@ -163,7 +163,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } SpecialAllocatorMethod::Realloc => { let [ptr, old_size, align, new_size] = this.check_shim_sig( - shim_sig_nounwind!(extern "Rust" fn(*_, usize, core::mem::Alignment, usize) -> *_), + shim_sig!(extern "Rust" fn(*_, usize, core::mem::Alignment, usize) -> *_), (link_name, abi, args), )?; let ptr = this.read_pointer(ptr)?; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index ac47fbe6d7b38..9eaadffb55922 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -318,7 +318,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // This is a no-op shim that only exists to prevent making the allocator shims // instantly stable. let [] = this.check_shim_sig( - shim_sig_nounwind!(extern "Rust" fn() -> ()), + shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args), )?; } diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index ca18bcb79fe0b..9f4aa426f8f3c 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -12,7 +12,6 @@ pub struct ShimSig<'tcx, const ARGS: usize> { pub abi: ExternAbi, pub args: [Ty<'tcx>; ARGS], pub ret: Ty<'tcx>, - pub nounwind: bool, pub c_variadic: bool, } @@ -36,24 +35,6 @@ macro_rules! shim_sig { abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"), args, ret: shim_sig_arg!(this, $($ret)*), - nounwind: false, - c_variadic, - } - } - }; -} - -/// Same as `shim_sig!` but promises that this function will not unwind, even if the ABI allows it. -#[macro_export] -macro_rules! shim_sig_nounwind { - (extern $abi:literal fn($($args:tt)*) -> $($ret:tt)*) => { - |this| { - let (args, c_variadic) = shim_sig_args_sep!(this, [$($args)*]); - $crate::shims::sig::ShimSig { - abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"), - args, - ret: shim_sig_arg!(this, $($ret)*), - nounwind: true, c_variadic, } } @@ -213,7 +194,6 @@ fn check_shim_abi<'tcx>( this: &MiriInterpCx<'tcx>, link_name: Symbol, callee_abi: &FnAbi<'tcx, Ty<'tcx>>, - callee_nounwind: bool, caller_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> InterpResult<'tcx> { if callee_abi.conv != caller_abi.conv { @@ -223,12 +203,9 @@ fn check_shim_abi<'tcx>( caller = caller_abi.conv, ); } - // FIXME: is this needed? Or is it enough to just check this if/when an actual unwind happens? - if callee_abi.can_unwind && !callee_nounwind && !caller_abi.can_unwind { - throw_ub_format!( - "ABI mismatch: callee may unwind, but caller asumes that no unwinding will occur", - ); - } + // No need to check unwinding: if the caller signature forbids unwinding, that's already + // reflected in the unwind destination so if an unwind occurs it will be reported as UB. + if caller_abi.c_variadic && !callee_abi.c_variadic { throw_ub_format!( "ABI mismatch: `{link_name}` is a non-variadic function, but the caller is using a variadic signature" @@ -352,7 +329,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let callee_fn_abi = shim_sig.as_abi(this); // Check everything. - check_shim_abi(this, link_name, callee_fn_abi, shim_sig.nounwind, caller_fn_abi)?; + check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?; this.check_shim_symbol_clash(link_name)?; // Return arguments. @@ -378,7 +355,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let callee_fn_abi = shim_sig.as_abi(this); // Check everything. - check_shim_abi(this, link_name, callee_fn_abi, shim_sig.nounwind, caller_fn_abi)?; + check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?; this.check_shim_symbol_clash(link_name)?; // Return arguments. diff --git a/src/tools/miri/tests/pass/function_calls/unwind_abi_mismatch.rs b/src/tools/miri/tests/pass/function_calls/unwind_abi_mismatch.rs new file mode 100644 index 0000000000000..ba6ebc2a85acf --- /dev/null +++ b/src/tools/miri/tests/pass/function_calls/unwind_abi_mismatch.rs @@ -0,0 +1,29 @@ +#![feature(rustc_attrs)] + +#[unsafe(no_mangle)] +extern "C-unwind" fn does_not_unwind_but_could() {} + +fn main() { + // Calling a maybe-unwinding function with a non-unwinding ABI is okay if the function + // does not actually unwind. See `tests/fail/panic/bad_unwind.rs` for the dual test that is UB. + let f: extern "C-unwind" fn() = does_not_unwind_but_could; + let f: extern "C" fn() = unsafe { std::mem::transmute(f) }; + f(); + + // The same applies when we call such a function via an extern import. + // This is the dual to `tests/fail/function_calls/exported_symbol_bad_unwind1.rs`. + extern "C" { + #[link_name = "does_not_unwind_but_could"] + fn imported(); + } + unsafe { imported() }; + + // The same does for shims: we can invoke maybe-unwinding shims via a non-unwinding declaration. + // We don't have "C-unwind" shims that we could import with "C" so the closest thing we can test + // are "Rust" shims imported via `#[rustc_nounwind]`. + extern "Rust" { + #[rustc_nounwind] + pub fn miri_spin_loop(); + } + unsafe { miri_spin_loop() }; +} diff --git a/src/tools/rustdoc/Cargo.toml b/src/tools/rustdoc/Cargo.toml index 681256665d688..c849625bda487 100644 --- a/src/tools/rustdoc/Cargo.toml +++ b/src/tools/rustdoc/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" # rustdoc a different name. [[bin]] -name = "rustdoc_tool_binary" +name = "rustdoc-tool-binary" path = "main.rs" [dependencies] diff --git a/tests/ui/traits/next-solver/coroutine-yield-unnormalized-alias.rs b/tests/ui/traits/next-solver/coroutine-yield-unnormalized-alias.rs new file mode 100644 index 0000000000000..5e22bf632097b --- /dev/null +++ b/tests/ui/traits/next-solver/coroutine-yield-unnormalized-alias.rs @@ -0,0 +1,27 @@ +//@ check-pass +//@ edition: 2024 +//@ compile-flags: -Znext-solver=globally +//! Regression test for #160652. Yielding an item from `impl Iterator` without an +//! explicit `Item` bound used to ICE in NLL type relating: both the MIR `yield_ty` +//! and the yielded local `i` were `::Item`. + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +fn iter() -> impl Iterator { + Some(()).into_iter() +} + +fn yield_unnormalized_item() -> impl Coroutine { + #[coroutine] + move || { + for i in iter() { + yield i + } + } +} + +fn main() { + let _ = yield_unnormalized_item(); +}