Auto merge of #138873 - jhpratt:rollup-tggrbxl, r=jhpratt

Rollup of 10 pull requests

Successful merges:

 - #137593 (fix download-llvm logic for subtree sync branches)
 - #137736 (Don't attempt to export compiler-builtins symbols from rust dylibs)
 - #138135 (Simplify `PartialOrd` on tuples containing primitives)
 - #138321 ([bootstrap] Distribute split debuginfo if present)
 - #138574 (rustdoc: be more strict about "Methods from Deref")
 - #138606 (Fix missing rustfmt in msi installer - cont)
 - #138671 (Fix `FileType` `PartialEq` implementation on Windows)
 - #138728 (Update `compiler-builtins` to 0.1.152)
 - #138783 (Cache current_dll_path output)
 - #138846 (Tweaks to writeback and `Obligation -> Goal` conversion)

Failed merges:

 - #138755 ([rustdoc] Remove duplicated loop when computing doc cfgs)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-03-24 01:03:21 +00:00
commit ae8ab87de4
34 changed files with 857 additions and 237 deletions

View File

@ -16,8 +16,8 @@ index 7165c3e48af..968552ad435 100644
[dependencies] [dependencies]
core = { path = "../core", public = true } core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std', 'no-f16-f128'] } +compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std', 'no-f16-f128'] }
[features] [features]
compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-mem = ['compiler_builtins/mem']

View File

@ -1782,7 +1782,10 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
let mut symbols = Vec::new(); let mut symbols = Vec::new();
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) { // Do not export mangled symbols from cdylibs and don't attempt to export compiler-builtins
// from any cdylib. The latter doesn't work anyway as we use hidden visibility for
// compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning.
if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) {
symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate( symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
tcx, symbol, cnum, tcx, symbol, cnum,
)); ));
@ -1821,7 +1824,9 @@ pub(crate) fn linked_symbols(
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) || info.used { if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum)
|| info.used
{
symbols.push(( symbols.push((
symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum), symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
info.kind, info.kind,

View File

@ -1,7 +1,7 @@
//! A utility module to inspect currently ambiguous obligations in the current context. //! A utility module to inspect currently ambiguous obligations in the current context.
use rustc_infer::traits::{self, ObligationCause, PredicateObligations}; use rustc_infer::traits::{self, ObligationCause, PredicateObligations};
use rustc_middle::traits::solve::{Goal, GoalSource}; use rustc_middle::traits::solve::GoalSource;
use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use rustc_span::Span; use rustc_span::Span;
use rustc_trait_selection::solve::inspect::{ use rustc_trait_selection::solve::inspect::{
@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
root_cause: &obligation.cause, root_cause: &obligation.cause,
}; };
let goal = Goal::new(self.tcx, obligation.param_env, obligation.predicate); let goal = obligation.as_goal();
self.visit_proof_tree(goal, &mut visitor); self.visit_proof_tree(goal, &mut visitor);
} }

View File

@ -548,7 +548,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
let fcx_typeck_results = self.fcx.typeck_results.borrow(); let fcx_typeck_results = self.fcx.typeck_results.borrow();
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
for (predicate, cause) in &fcx_typeck_results.coroutine_stalled_predicates { for (predicate, cause) in &fcx_typeck_results.coroutine_stalled_predicates {
let (predicate, cause) = self.resolve((*predicate, cause.clone()), &cause.span); let (predicate, cause) =
self.resolve_coroutine_predicate((*predicate, cause.clone()), &cause.span);
self.typeck_results.coroutine_stalled_predicates.insert((predicate, cause)); self.typeck_results.coroutine_stalled_predicates.insert((predicate, cause));
} }
} }
@ -730,7 +731,25 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
T: TypeFoldable<TyCtxt<'tcx>>, T: TypeFoldable<TyCtxt<'tcx>>,
{ {
let value = self.fcx.resolve_vars_if_possible(value); let value = self.fcx.resolve_vars_if_possible(value);
let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body)); let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body, true));
assert!(!value.has_infer());
// We may have introduced e.g. `ty::Error`, if inference failed, make sure
// to mark the `TypeckResults` as tainted in that case, so that downstream
// users of the typeck results don't produce extra errors, or worse, ICEs.
if let Err(guar) = value.error_reported() {
self.typeck_results.tainted_by_errors = Some(guar);
}
value
}
fn resolve_coroutine_predicate<T>(&mut self, value: T, span: &dyn Locatable) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let value = self.fcx.resolve_vars_if_possible(value);
let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body, false));
assert!(!value.has_infer()); assert!(!value.has_infer());
// We may have introduced e.g. `ty::Error`, if inference failed, make sure // We may have introduced e.g. `ty::Error`, if inference failed, make sure
@ -774,8 +793,9 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
fcx: &'cx FnCtxt<'cx, 'tcx>, fcx: &'cx FnCtxt<'cx, 'tcx>,
span: &'cx dyn Locatable, span: &'cx dyn Locatable,
body: &'tcx hir::Body<'tcx>, body: &'tcx hir::Body<'tcx>,
should_normalize: bool,
) -> Resolver<'cx, 'tcx> { ) -> Resolver<'cx, 'tcx> {
Resolver { fcx, span, body, should_normalize: fcx.next_trait_solver() } Resolver { fcx, span, body, should_normalize }
} }
fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed { fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed {
@ -805,10 +825,9 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
T: Into<ty::GenericArg<'tcx>> + TypeSuperFoldable<TyCtxt<'tcx>> + Copy, T: Into<ty::GenericArg<'tcx>> + TypeSuperFoldable<TyCtxt<'tcx>> + Copy,
{ {
let tcx = self.fcx.tcx; let tcx = self.fcx.tcx;
// We must deeply normalize in the new solver, since later lints // We must deeply normalize in the new solver, since later lints expect
// expect that types that show up in the typeck are fully // that types that show up in the typeck are fully normalized.
// normalized. let mut value = if self.should_normalize && self.fcx.next_trait_solver() {
let mut value = if self.should_normalize {
let body_id = tcx.hir_body_owner_def_id(self.body.id()); let body_id = tcx.hir_body_owner_def_id(self.body.id());
let cause = ObligationCause::misc(self.span.to_span(tcx), body_id); let cause = ObligationCause::misc(self.span.to_span(tcx), body_id);
let at = self.fcx.at(&cause, self.fcx.param_env); let at = self.fcx.at(&cause, self.fcx.param_env);
@ -864,20 +883,15 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
} }
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
self.handle_term(ct, ty::Const::outer_exclusive_binder, |tcx, guar| { self.handle_term(ct, ty::Const::outer_exclusive_binder, ty::Const::new_error)
ty::Const::new_error(tcx, guar)
})
.super_fold_with(self)
} }
fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
// Do not normalize predicates in the new solver. The new solver is assert!(
// supposed to handle unnormalized predicates and incorrectly normalizing !self.should_normalize,
// them can be unsound, e.g. for `WellFormed` predicates. "normalizing predicates in writeback is not generally sound"
let prev = mem::replace(&mut self.should_normalize, false); );
let predicate = predicate.super_fold_with(self); predicate.super_fold_with(self)
self.should_normalize = prev;
predicate
} }
} }

View File

@ -246,8 +246,7 @@ impl<'tcx> InferCtxt<'tcx> {
.eq(DefineOpaqueTypes::Yes, prev, hidden_ty)? .eq(DefineOpaqueTypes::Yes, prev, hidden_ty)?
.obligations .obligations
.into_iter() .into_iter()
// FIXME: Shuttling between obligations and goals is awkward. .map(|obligation| obligation.as_goal()),
.map(Goal::from),
); );
} }
} }

View File

@ -54,6 +54,12 @@ pub struct Obligation<'tcx, T> {
pub recursion_depth: usize, pub recursion_depth: usize,
} }
impl<'tcx, T: Copy> Obligation<'tcx, T> {
pub fn as_goal(&self) -> solve::Goal<'tcx, T> {
solve::Goal { param_env: self.param_env, predicate: self.predicate }
}
}
impl<'tcx, T: PartialEq> PartialEq<Obligation<'tcx, T>> for Obligation<'tcx, T> { impl<'tcx, T: PartialEq> PartialEq<Obligation<'tcx, T>> for Obligation<'tcx, T> {
#[inline] #[inline]
fn eq(&self, other: &Obligation<'tcx, T>) -> bool { fn eq(&self, other: &Obligation<'tcx, T>) -> bool {
@ -75,12 +81,6 @@ impl<T: Hash> Hash for Obligation<'_, T> {
} }
} }
impl<'tcx, P> From<Obligation<'tcx, P>> for solve::Goal<'tcx, P> {
fn from(value: Obligation<'tcx, P>) -> Self {
solve::Goal { param_env: value.param_env, predicate: value.predicate }
}
}
pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>; pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitPredicate<'tcx>>; pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitPredicate<'tcx>>;
pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;

View File

@ -60,66 +60,76 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
#[cfg(unix)] #[cfg(unix)]
fn current_dll_path() -> Result<PathBuf, String> { fn current_dll_path() -> Result<PathBuf, String> {
use std::ffi::{CStr, OsStr}; use std::sync::OnceLock;
use std::os::unix::prelude::*;
#[cfg(not(target_os = "aix"))] // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr`
unsafe { // needs to iterate over the symbol table of librustc_driver.so until it finds a match.
let addr = current_dll_path as usize as *mut _; // As such cache this to avoid recomputing if we try to get the sysroot in multiple places.
let mut info = std::mem::zeroed(); static CURRENT_DLL_PATH: OnceLock<Result<PathBuf, String>> = OnceLock::new();
if libc::dladdr(addr, &mut info) == 0 { CURRENT_DLL_PATH
return Err("dladdr failed".into()); .get_or_init(|| {
} use std::ffi::{CStr, OsStr};
if info.dli_fname.is_null() { use std::os::unix::prelude::*;
return Err("dladdr returned null pointer".into());
}
let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
let os = OsStr::from_bytes(bytes);
Ok(PathBuf::from(os))
}
#[cfg(target_os = "aix")] #[cfg(not(target_os = "aix"))]
unsafe { unsafe {
// On AIX, the symbol `current_dll_path` references a function descriptor. let addr = current_dll_path as usize as *mut _;
// A function descriptor is consisted of (See https://reviews.llvm.org/D62532) let mut info = std::mem::zeroed();
// * The address of the entry point of the function. if libc::dladdr(addr, &mut info) == 0 {
// * The TOC base address for the function. return Err("dladdr failed".into());
// * The environment pointer.
// The function descriptor is in the data section.
let addr = current_dll_path as u64;
let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
loop {
if libc::loadquery(
libc::L_GETINFO,
buffer.as_mut_ptr() as *mut u8,
(size_of::<libc::ld_info>() * buffer.len()) as u32,
) >= 0
{
break;
} else {
if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
return Err("loadquery failed".into());
} }
buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>()); if info.dli_fname.is_null() {
} return Err("dladdr returned null pointer".into());
} }
let mut current = buffer.as_mut_ptr() as *mut libc::ld_info; let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
loop {
let data_base = (*current).ldinfo_dataorg as u64;
let data_end = data_base + (*current).ldinfo_datasize;
if (data_base..data_end).contains(&addr) {
let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
let os = OsStr::from_bytes(bytes); let os = OsStr::from_bytes(bytes);
return Ok(PathBuf::from(os)); Ok(PathBuf::from(os))
} }
if (*current).ldinfo_next == 0 {
break; #[cfg(target_os = "aix")]
unsafe {
// On AIX, the symbol `current_dll_path` references a function descriptor.
// A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
// * The address of the entry point of the function.
// * The TOC base address for the function.
// * The environment pointer.
// The function descriptor is in the data section.
let addr = current_dll_path as u64;
let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
loop {
if libc::loadquery(
libc::L_GETINFO,
buffer.as_mut_ptr() as *mut u8,
(size_of::<libc::ld_info>() * buffer.len()) as u32,
) >= 0
{
break;
} else {
if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
return Err("loadquery failed".into());
}
buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>());
}
}
let mut current = buffer.as_mut_ptr() as *mut libc::ld_info;
loop {
let data_base = (*current).ldinfo_dataorg as u64;
let data_end = data_base + (*current).ldinfo_datasize;
if (data_base..data_end).contains(&addr) {
let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
let os = OsStr::from_bytes(bytes);
return Ok(PathBuf::from(os));
}
if (*current).ldinfo_next == 0 {
break;
}
current = (current as *mut i8).offset((*current).ldinfo_next as isize)
as *mut libc::ld_info;
}
return Err(format!("current dll's address {} is not in the load map", addr));
} }
current = })
(current as *mut i8).offset((*current).ldinfo_next as isize) as *mut libc::ld_info; .clone()
}
return Err(format!("current dll's address {} is not in the load map", addr));
}
} }
#[cfg(windows)] #[cfg(windows)]

View File

@ -96,7 +96,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> { ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID) crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
.map(|obligations| { .map(|obligations| {
obligations.into_iter().map(|obligation| obligation.into()).collect() obligations.into_iter().map(|obligation| obligation.as_goal()).collect()
}) })
} }

View File

@ -80,7 +80,7 @@ impl<'tcx> ObligationStorage<'tcx> {
// change. // change.
// FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed. // FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| { self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| {
let goal = o.clone().into(); let goal = o.as_goal();
let result = <&SolverDelegate<'tcx>>::from(infcx) let result = <&SolverDelegate<'tcx>>::from(infcx)
.evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span) .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span)
.0; .0;
@ -161,7 +161,7 @@ where
let mut has_changed = false; let mut has_changed = false;
for obligation in self.obligations.unstalled_for_select() { for obligation in self.obligations.unstalled_for_select() {
let goal = obligation.clone().into(); let goal = obligation.as_goal();
let result = <&SolverDelegate<'tcx>>::from(infcx) let result = <&SolverDelegate<'tcx>>::from(infcx)
.evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span)
.0; .0;

View File

@ -10,7 +10,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _}; use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
use rustc_type_ir::solve::{Goal, NoSolution}; use rustc_type_ir::solve::NoSolution;
use tracing::{instrument, trace}; use tracing::{instrument, trace};
use crate::solve::Certainty; use crate::solve::Certainty;
@ -89,7 +89,7 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
let (code, refine_obligation) = infcx.probe(|_| { let (code, refine_obligation) = infcx.probe(|_| {
match <&SolverDelegate<'tcx>>::from(infcx) match <&SolverDelegate<'tcx>>::from(infcx)
.evaluate_root_goal( .evaluate_root_goal(
root_obligation.clone().into(), root_obligation.as_goal(),
GenerateProofTree::No, GenerateProofTree::No,
root_obligation.cause.span, root_obligation.cause.span,
) )
@ -155,7 +155,7 @@ fn find_best_leaf_obligation<'tcx>(
.fudge_inference_if_ok(|| { .fudge_inference_if_ok(|| {
infcx infcx
.visit_proof_tree( .visit_proof_tree(
obligation.clone().into(), obligation.as_goal(),
&mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, &mut BestObligation { obligation: obligation.clone(), consider_ambiguities },
) )
.break_value() .break_value()
@ -245,7 +245,7 @@ impl<'tcx> BestObligation<'tcx> {
{ {
let nested_goal = candidate.instantiate_proof_tree_for_nested_goal( let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
GoalSource::Misc, GoalSource::Misc,
Goal::new(infcx.tcx, obligation.param_env, obligation.predicate), obligation.as_goal(),
self.span(), self.span(),
); );
// Skip nested goals that aren't the *reason* for our goal's failure. // Skip nested goals that aren't the *reason* for our goal's failure.

View File

@ -625,7 +625,7 @@ fn compute_intercrate_ambiguity_causes<'tcx>(
let mut causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>> = Default::default(); let mut causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>> = Default::default();
for obligation in obligations { for obligation in obligations {
search_ambiguity_causes(infcx, obligation.clone().into(), &mut causes); search_ambiguity_causes(infcx, obligation.as_goal(), &mut causes);
} }
causes causes

View File

@ -67,9 +67,9 @@ dependencies = [
[[package]] [[package]]
name = "compiler_builtins" name = "compiler_builtins"
version = "0.1.151" version = "0.1.152"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abc30f1766d387c35f2405e586d3e7a88230dc728ff78cd1d0bc59ae0b63154b" checksum = "2153cf213eb259361567720ce55f6446f17acd0ccca87fb6dc05360578228a58"
dependencies = [ dependencies = [
"cc", "cc",
"rustc-std-workspace-core", "rustc-std-workspace-core",

View File

@ -16,7 +16,7 @@ bench = false
[dependencies] [dependencies]
core = { path = "../core", public = true } core = { path = "../core", public = true }
compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std'] } compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std'] }
[features] [features]
compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-mem = ['compiler_builtins/mem']

View File

@ -29,6 +29,7 @@ mod bytewise;
pub(crate) use bytewise::BytewiseEq; pub(crate) use bytewise::BytewiseEq;
use self::Ordering::*; use self::Ordering::*;
use crate::ops::ControlFlow;
/// Trait for comparisons using the equality operator. /// Trait for comparisons using the equality operator.
/// ///
@ -1435,6 +1436,67 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
fn ge(&self, other: &Rhs) -> bool { fn ge(&self, other: &Rhs) -> bool {
self.partial_cmp(other).is_some_and(Ordering::is_ge) self.partial_cmp(other).is_some_and(Ordering::is_ge)
} }
/// If `self == other`, returns `ControlFlow::Continue(())`.
/// Otherwise, returns `ControlFlow::Break(self < other)`.
///
/// This is useful for chaining together calls when implementing a lexical
/// `PartialOrd::lt`, as it allows types (like primitives) which can cheaply
/// check `==` and `<` separately to do rather than needing to calculate
/// (then optimize out) the three-way `Ordering` result.
#[inline]
#[must_use]
// Added to improve the behaviour of tuples; not necessarily stabilization-track.
#[unstable(feature = "partial_ord_chaining_methods", issue = "none")]
#[doc(hidden)]
fn __chaining_lt(&self, other: &Rhs) -> ControlFlow<bool> {
default_chaining_impl(self, other, Ordering::is_lt)
}
/// Same as `__chaining_lt`, but for `<=` instead of `<`.
#[inline]
#[must_use]
#[unstable(feature = "partial_ord_chaining_methods", issue = "none")]
#[doc(hidden)]
fn __chaining_le(&self, other: &Rhs) -> ControlFlow<bool> {
default_chaining_impl(self, other, Ordering::is_le)
}
/// Same as `__chaining_lt`, but for `>` instead of `<`.
#[inline]
#[must_use]
#[unstable(feature = "partial_ord_chaining_methods", issue = "none")]
#[doc(hidden)]
fn __chaining_gt(&self, other: &Rhs) -> ControlFlow<bool> {
default_chaining_impl(self, other, Ordering::is_gt)
}
/// Same as `__chaining_lt`, but for `>=` instead of `<`.
#[inline]
#[must_use]
#[unstable(feature = "partial_ord_chaining_methods", issue = "none")]
#[doc(hidden)]
fn __chaining_ge(&self, other: &Rhs) -> ControlFlow<bool> {
default_chaining_impl(self, other, Ordering::is_ge)
}
}
fn default_chaining_impl<T: ?Sized, U: ?Sized>(
lhs: &T,
rhs: &U,
p: impl FnOnce(Ordering) -> bool,
) -> ControlFlow<bool>
where
T: PartialOrd<U>,
{
// It's important that this only call `partial_cmp` once, not call `eq` then
// one of the relational operators. We don't want to `bcmp`-then-`memcp` a
// `String`, for example, or similarly for other data structures (#108157).
match <T as PartialOrd<U>>::partial_cmp(lhs, rhs) {
Some(Equal) => ControlFlow::Continue(()),
Some(c) => ControlFlow::Break(p(c)),
None => ControlFlow::Break(false),
}
} }
/// Derive macro generating an impl of the trait [`PartialOrd`]. /// Derive macro generating an impl of the trait [`PartialOrd`].
@ -1741,6 +1803,7 @@ where
mod impls { mod impls {
use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::cmp::Ordering::{self, Equal, Greater, Less};
use crate::hint::unreachable_unchecked; use crate::hint::unreachable_unchecked;
use crate::ops::ControlFlow::{self, Break, Continue};
macro_rules! partial_eq_impl { macro_rules! partial_eq_impl {
($($t:ty)*) => ($( ($($t:ty)*) => ($(
@ -1779,6 +1842,35 @@ mod impls {
eq_impl! { () bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } eq_impl! { () bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 }
macro_rules! chaining_methods_impl {
($t:ty) => {
// These implementations are the same for `Ord` or `PartialOrd` types
// because if either is NAN the `==` test will fail so we end up in
// the `Break` case and the comparison will correctly return `false`.
#[inline]
fn __chaining_lt(&self, other: &Self) -> ControlFlow<bool> {
let (lhs, rhs) = (*self, *other);
if lhs == rhs { Continue(()) } else { Break(lhs < rhs) }
}
#[inline]
fn __chaining_le(&self, other: &Self) -> ControlFlow<bool> {
let (lhs, rhs) = (*self, *other);
if lhs == rhs { Continue(()) } else { Break(lhs <= rhs) }
}
#[inline]
fn __chaining_gt(&self, other: &Self) -> ControlFlow<bool> {
let (lhs, rhs) = (*self, *other);
if lhs == rhs { Continue(()) } else { Break(lhs > rhs) }
}
#[inline]
fn __chaining_ge(&self, other: &Self) -> ControlFlow<bool> {
let (lhs, rhs) = (*self, *other);
if lhs == rhs { Continue(()) } else { Break(lhs >= rhs) }
}
};
}
macro_rules! partial_ord_impl { macro_rules! partial_ord_impl {
($($t:ty)*) => ($( ($($t:ty)*) => ($(
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -1800,6 +1892,8 @@ mod impls {
fn ge(&self, other: &$t) -> bool { (*self) >= (*other) } fn ge(&self, other: &$t) -> bool { (*self) >= (*other) }
#[inline(always)] #[inline(always)]
fn gt(&self, other: &$t) -> bool { (*self) > (*other) } fn gt(&self, other: &$t) -> bool { (*self) > (*other) }
chaining_methods_impl!($t);
} }
)*) )*)
} }
@ -1838,6 +1932,8 @@ mod impls {
fn ge(&self, other: &$t) -> bool { (*self) >= (*other) } fn ge(&self, other: &$t) -> bool { (*self) >= (*other) }
#[inline(always)] #[inline(always)]
fn gt(&self, other: &$t) -> bool { (*self) > (*other) } fn gt(&self, other: &$t) -> bool { (*self) > (*other) }
chaining_methods_impl!($t);
} }
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]

View File

@ -2,6 +2,7 @@
use crate::cmp::Ordering::{self, *}; use crate::cmp::Ordering::{self, *};
use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy}; use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy};
use crate::ops::ControlFlow::{Break, Continue};
// Recursive macro for implementing n-ary tuple functions and operations // Recursive macro for implementing n-ary tuple functions and operations
// //
@ -80,19 +81,19 @@ macro_rules! tuple_impls {
} }
#[inline] #[inline]
fn lt(&self, other: &($($T,)+)) -> bool { fn lt(&self, other: &($($T,)+)) -> bool {
lexical_ord!(lt, Less, $( ${ignore($T)} self.${index()}, other.${index()} ),+) lexical_ord!(lt, __chaining_lt, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
} }
#[inline] #[inline]
fn le(&self, other: &($($T,)+)) -> bool { fn le(&self, other: &($($T,)+)) -> bool {
lexical_ord!(le, Less, $( ${ignore($T)} self.${index()}, other.${index()} ),+) lexical_ord!(le, __chaining_le, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
} }
#[inline] #[inline]
fn ge(&self, other: &($($T,)+)) -> bool { fn ge(&self, other: &($($T,)+)) -> bool {
lexical_ord!(ge, Greater, $( ${ignore($T)} self.${index()}, other.${index()} ),+) lexical_ord!(ge, __chaining_ge, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
} }
#[inline] #[inline]
fn gt(&self, other: &($($T,)+)) -> bool { fn gt(&self, other: &($($T,)+)) -> bool {
lexical_ord!(gt, Greater, $( ${ignore($T)} self.${index()}, other.${index()} ),+) lexical_ord!(gt, __chaining_gt, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
} }
} }
} }
@ -171,15 +172,16 @@ macro_rules! maybe_tuple_doc {
// `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, opt_is_lt, a1, b1, // `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, opt_is_lt, a1, b1,
// a2, b2, a3, b3)` (and similarly for `lexical_cmp`) // a2, b2, a3, b3)` (and similarly for `lexical_cmp`)
// //
// `$ne_rel` is only used to determine the result after checking that they're // `$chain_rel` is the chaining method from `PartialOrd` to use for all but the
// not equal, so `lt` and `le` can both just use `Less`. // final value, to produce better results for simple primitives.
macro_rules! lexical_ord { macro_rules! lexical_ord {
($rel: ident, $ne_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{ ($rel: ident, $chain_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{
let c = PartialOrd::partial_cmp(&$a, &$b); match PartialOrd::$chain_rel(&$a, &$b) {
if c != Some(Equal) { c == Some($ne_rel) } Break(val) => val,
else { lexical_ord!($rel, $ne_rel, $($rest_a, $rest_b),+) } Continue(()) => lexical_ord!($rel, $chain_rel, $($rest_a, $rest_b),+),
}
}}; }};
($rel: ident, $ne_rel: ident, $a:expr, $b:expr) => { ($rel: ident, $chain_rel: ident, $a:expr, $b:expr) => {
// Use the specific method for the last element // Use the specific method for the last element
PartialOrd::$rel(&$a, &$b) PartialOrd::$rel(&$a, &$b)
}; };

View File

@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
panic_unwind = { path = "../panic_unwind", optional = true } panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" } panic_abort = { path = "../panic_abort" }
core = { path = "../core", public = true } core = { path = "../core", public = true }
compiler_builtins = { version = "=0.1.151" } compiler_builtins = { version = "=0.1.152" }
unwind = { path = "../unwind" } unwind = { path = "../unwind" }
hashbrown = { version = "0.15", default-features = false, features = [ hashbrown = { version = "0.15", default-features = false, features = [
'rustc-dep-of-std', 'rustc-dep-of-std',

View File

@ -1719,6 +1719,23 @@ fn test_eq_direntry_metadata() {
} }
} }
/// Test that windows file type equality is not affected by attributes unrelated
/// to the file type.
#[test]
#[cfg(target_os = "windows")]
fn test_eq_windows_file_type() {
let tmpdir = tmpdir();
let file1 = File::create(tmpdir.join("file1")).unwrap();
let file2 = File::create(tmpdir.join("file2")).unwrap();
assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type());
// Change the readonly attribute of one file.
let mut perms = file1.metadata().unwrap().permissions();
perms.set_readonly(true);
file1.set_permissions(perms).unwrap();
assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type());
}
/// Regression test for https://github.com/rust-lang/rust/issues/50619. /// Regression test for https://github.com/rust-lang/rust/issues/50619.
#[test] #[test]
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]

View File

@ -41,8 +41,8 @@ pub struct FileAttr {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct FileType { pub struct FileType {
attributes: u32, is_directory: bool,
reparse_tag: u32, is_symlink: bool,
} }
pub struct ReadDir { pub struct ReadDir {
@ -1111,32 +1111,29 @@ impl FileTimes {
} }
impl FileType { impl FileType {
fn new(attrs: u32, reparse_tag: u32) -> FileType { fn new(attributes: u32, reparse_tag: u32) -> FileType {
FileType { attributes: attrs, reparse_tag } let is_directory = attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0;
let is_symlink = {
let is_reparse_point = attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0;
let is_reparse_tag_name_surrogate = reparse_tag & 0x20000000 != 0;
is_reparse_point && is_reparse_tag_name_surrogate
};
FileType { is_directory, is_symlink }
} }
pub fn is_dir(&self) -> bool { pub fn is_dir(&self) -> bool {
!self.is_symlink() && self.is_directory() !self.is_symlink && self.is_directory
} }
pub fn is_file(&self) -> bool { pub fn is_file(&self) -> bool {
!self.is_symlink() && !self.is_directory() !self.is_symlink && !self.is_directory
} }
pub fn is_symlink(&self) -> bool { pub fn is_symlink(&self) -> bool {
self.is_reparse_point() && self.is_reparse_tag_name_surrogate() self.is_symlink
} }
pub fn is_symlink_dir(&self) -> bool { pub fn is_symlink_dir(&self) -> bool {
self.is_symlink() && self.is_directory() self.is_symlink && self.is_directory
} }
pub fn is_symlink_file(&self) -> bool { pub fn is_symlink_file(&self) -> bool {
self.is_symlink() && !self.is_directory() self.is_symlink && !self.is_directory
}
fn is_directory(&self) -> bool {
self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0
}
fn is_reparse_point(&self) -> bool {
self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
}
fn is_reparse_tag_name_surrogate(&self) -> bool {
self.reparse_tag & 0x20000000 != 0
} }
} }

View File

@ -33,7 +33,7 @@ use crate::utils::exec::command;
use crate::utils::helpers::{ use crate::utils::helpers::{
exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date,
}; };
use crate::{CLang, Compiler, DependencyType, GitRepo, LLVM_TOOLS, Mode, debug, trace}; use crate::{CLang, Compiler, DependencyType, FileType, GitRepo, LLVM_TOOLS, Mode, debug, trace};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Std { pub struct Std {
@ -321,7 +321,7 @@ fn copy_and_stamp(
dependency_type: DependencyType, dependency_type: DependencyType,
) { ) {
let target = libdir.join(name); let target = libdir.join(name);
builder.copy_link(&sourcedir.join(name), &target); builder.copy_link(&sourcedir.join(name), &target, FileType::Regular);
target_deps.push((target, dependency_type)); target_deps.push((target, dependency_type));
} }
@ -330,7 +330,7 @@ fn copy_llvm_libunwind(builder: &Builder<'_>, target: TargetSelection, libdir: &
let libunwind_path = builder.ensure(llvm::Libunwind { target }); let libunwind_path = builder.ensure(llvm::Libunwind { target });
let libunwind_source = libunwind_path.join("libunwind.a"); let libunwind_source = libunwind_path.join("libunwind.a");
let libunwind_target = libdir.join("libunwind.a"); let libunwind_target = libdir.join("libunwind.a");
builder.copy_link(&libunwind_source, &libunwind_target); builder.copy_link(&libunwind_source, &libunwind_target, FileType::NativeLibrary);
libunwind_target libunwind_target
} }
@ -401,7 +401,7 @@ fn copy_self_contained_objects(
for &obj in &["crtbegin.o", "crtbeginS.o", "crtend.o", "crtendS.o"] { for &obj in &["crtbegin.o", "crtbeginS.o", "crtend.o", "crtendS.o"] {
let src = crt_path.join(obj); let src = crt_path.join(obj);
let target = libdir_self_contained.join(obj); let target = libdir_self_contained.join(obj);
builder.copy_link(&src, &target); builder.copy_link(&src, &target, FileType::NativeLibrary);
target_deps.push((target, DependencyType::TargetSelfContained)); target_deps.push((target, DependencyType::TargetSelfContained));
} }
} else { } else {
@ -443,9 +443,9 @@ fn copy_self_contained_objects(
} else if target.is_windows_gnu() { } else if target.is_windows_gnu() {
for obj in ["crt2.o", "dllcrt2.o"].iter() { for obj in ["crt2.o", "dllcrt2.o"].iter() {
let src = compiler_file(builder, &builder.cc(target), target, CLang::C, obj); let src = compiler_file(builder, &builder.cc(target), target, CLang::C, obj);
let target = libdir_self_contained.join(obj); let dst = libdir_self_contained.join(obj);
builder.copy_link(&src, &target); builder.copy_link(&src, &dst, FileType::NativeLibrary);
target_deps.push((target, DependencyType::TargetSelfContained)); target_deps.push((dst, DependencyType::TargetSelfContained));
} }
} }
@ -790,8 +790,11 @@ impl Step for StdLink {
let file = t!(file); let file = t!(file);
let path = file.path(); let path = file.path();
if path.is_file() { if path.is_file() {
builder builder.copy_link(
.copy_link(&path, &sysroot.join("lib").join(path.file_name().unwrap())); &path,
&sysroot.join("lib").join(path.file_name().unwrap()),
FileType::Regular,
);
} }
} }
} }
@ -829,7 +832,7 @@ fn copy_sanitizers(
for runtime in &runtimes { for runtime in &runtimes {
let dst = libdir.join(&runtime.name); let dst = libdir.join(&runtime.name);
builder.copy_link(&runtime.path, &dst); builder.copy_link(&runtime.path, &dst, FileType::NativeLibrary);
// The `aarch64-apple-ios-macabi` and `x86_64-apple-ios-macabi` are also supported for // The `aarch64-apple-ios-macabi` and `x86_64-apple-ios-macabi` are also supported for
// sanitizers, but they share a sanitizer runtime with `${arch}-apple-darwin`, so we do // sanitizers, but they share a sanitizer runtime with `${arch}-apple-darwin`, so we do
@ -934,9 +937,9 @@ impl Step for StartupObjects {
.run(builder); .run(builder);
} }
let target = sysroot_dir.join((*file).to_string() + ".o"); let obj = sysroot_dir.join((*file).to_string() + ".o");
builder.copy_link(dst_file, &target); builder.copy_link(dst_file, &obj, FileType::NativeLibrary);
target_deps.push((target, DependencyType::Target)); target_deps.push((obj, DependencyType::Target));
} }
target_deps target_deps
@ -952,7 +955,7 @@ fn cp_rustc_component_to_ci_sysroot(builder: &Builder<'_>, sysroot: &Path, conte
if src.is_dir() { if src.is_dir() {
t!(fs::create_dir_all(dst)); t!(fs::create_dir_all(dst));
} else { } else {
builder.copy_link(&src, &dst); builder.copy_link(&src, &dst, FileType::Regular);
} }
} }
} }
@ -1707,7 +1710,7 @@ fn copy_codegen_backends_to_sysroot(
let dot = filename.find('.').unwrap(); let dot = filename.find('.').unwrap();
format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..])
}; };
builder.copy_link(file, &dst.join(target_filename)); builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary);
} }
} }
@ -2011,7 +2014,11 @@ impl Step for Assemble {
extra_features: vec![], extra_features: vec![],
}); });
let tool_exe = exe("llvm-bitcode-linker", target_compiler.host); let tool_exe = exe("llvm-bitcode-linker", target_compiler.host);
builder.copy_link(&llvm_bitcode_linker.tool_path, &libdir_bin.join(tool_exe)); builder.copy_link(
&llvm_bitcode_linker.tool_path,
&libdir_bin.join(tool_exe),
FileType::Executable,
);
} }
}; };
@ -2072,8 +2079,8 @@ impl Step for Assemble {
builder.sysroot_target_libdir(target_compiler, target_compiler.host); builder.sysroot_target_libdir(target_compiler, target_compiler.host);
let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext); let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext);
let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext); let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext);
builder.copy_link(&src_lib, &dst_lib); builder.copy_link(&src_lib, &dst_lib, FileType::NativeLibrary);
builder.copy_link(&src_lib, &target_dst_lib); builder.copy_link(&src_lib, &target_dst_lib, FileType::NativeLibrary);
} }
// Build the libraries for this compiler to link to (i.e., the libraries // Build the libraries for this compiler to link to (i.e., the libraries
@ -2168,7 +2175,7 @@ impl Step for Assemble {
}; };
if is_dylib_or_debug && can_be_rustc_dynamic_dep && !is_proc_macro { if is_dylib_or_debug && can_be_rustc_dynamic_dep && !is_proc_macro {
builder.copy_link(&f.path(), &rustc_libdir.join(&filename)); builder.copy_link(&f.path(), &rustc_libdir.join(&filename), FileType::Regular);
} }
} }
@ -2196,7 +2203,11 @@ impl Step for Assemble {
// See <https://github.com/rust-lang/rust/issues/132719>. // See <https://github.com/rust-lang/rust/issues/132719>.
let src_exe = exe("llvm-objcopy", target_compiler.host); let src_exe = exe("llvm-objcopy", target_compiler.host);
let dst_exe = exe("rust-objcopy", target_compiler.host); let dst_exe = exe("rust-objcopy", target_compiler.host);
builder.copy_link(&libdir_bin.join(src_exe), &libdir_bin.join(dst_exe)); builder.copy_link(
&libdir_bin.join(src_exe),
&libdir_bin.join(dst_exe),
FileType::Executable,
);
} }
// In addition to `rust-lld` also install `wasm-component-ld` when // In addition to `rust-lld` also install `wasm-component-ld` when
@ -2212,6 +2223,7 @@ impl Step for Assemble {
builder.copy_link( builder.copy_link(
&wasm_component.tool_path, &wasm_component.tool_path,
&libdir_bin.join(wasm_component.tool_path.file_name().unwrap()), &libdir_bin.join(wasm_component.tool_path.file_name().unwrap()),
FileType::Executable,
); );
} }
@ -2234,7 +2246,7 @@ impl Step for Assemble {
t!(fs::create_dir_all(bindir)); t!(fs::create_dir_all(bindir));
let compiler = builder.rustc(target_compiler); let compiler = builder.rustc(target_compiler);
debug!(src = ?rustc, dst = ?compiler, "linking compiler binary itself"); debug!(src = ?rustc, dst = ?compiler, "linking compiler binary itself");
builder.copy_link(&rustc, &compiler); builder.copy_link(&rustc, &compiler, FileType::Executable);
target_compiler target_compiler
} }
@ -2260,7 +2272,7 @@ pub fn add_to_sysroot(
DependencyType::Target => sysroot_dst, DependencyType::Target => sysroot_dst,
DependencyType::TargetSelfContained => self_contained_dst, DependencyType::TargetSelfContained => self_contained_dst,
}; };
builder.copy_link(&path, &dst.join(path.file_name().unwrap())); builder.copy_link(&path, &dst.join(path.file_name().unwrap()), FileType::Regular);
} }
} }

View File

@ -32,7 +32,7 @@ use crate::utils::helpers::{
exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit, exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit,
}; };
use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball}; use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball};
use crate::{Compiler, DependencyType, LLVM_TOOLS, Mode, trace}; use crate::{Compiler, DependencyType, FileType, LLVM_TOOLS, Mode, trace};
pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { pub fn pkgname(builder: &Builder<'_>, component: &str) -> String {
format!("{}-{}", component, builder.rust_package_vers()) format!("{}-{}", component, builder.rust_package_vers())
@ -81,7 +81,7 @@ impl Step for Docs {
let mut tarball = Tarball::new(builder, "rust-docs", &host.triple); let mut tarball = Tarball::new(builder, "rust-docs", &host.triple);
tarball.set_product_name("Rust Documentation"); tarball.set_product_name("Rust Documentation");
tarball.add_bulk_dir(builder.doc_out(host), dest); tarball.add_bulk_dir(builder.doc_out(host), dest);
tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, 0o644); tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, FileType::Regular);
Some(tarball.generate()) Some(tarball.generate())
} }
} }
@ -418,7 +418,7 @@ impl Step for Rustc {
.is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc"))
{ {
let rustdoc = builder.rustdoc(compiler); let rustdoc = builder.rustdoc(compiler);
builder.install(&rustdoc, &image.join("bin"), 0o755); builder.install(&rustdoc, &image.join("bin"), FileType::Executable);
} }
if let Some(ra_proc_macro_srv) = builder.ensure_if_default( if let Some(ra_proc_macro_srv) = builder.ensure_if_default(
@ -432,7 +432,8 @@ impl Step for Rustc {
}, },
builder.kind, builder.kind,
) { ) {
builder.install(&ra_proc_macro_srv.tool_path, &image.join("libexec"), 0o755); let dst = image.join("libexec");
builder.install(&ra_proc_macro_srv.tool_path, &dst, FileType::Executable);
} }
let libdir_relative = builder.libdir_relative(compiler); let libdir_relative = builder.libdir_relative(compiler);
@ -444,7 +445,7 @@ impl Step for Rustc {
if is_dylib(&entry.path()) { if is_dylib(&entry.path()) {
// Don't use custom libdir here because ^lib/ will be resolved again // Don't use custom libdir here because ^lib/ will be resolved again
// with installer // with installer
builder.install(&entry.path(), &image.join("lib"), 0o644); builder.install(&entry.path(), &image.join("lib"), FileType::NativeLibrary);
} }
} }
} }
@ -463,7 +464,11 @@ impl Step for Rustc {
if builder.config.lld_enabled { if builder.config.lld_enabled {
let src_dir = builder.sysroot_target_bindir(compiler, host); let src_dir = builder.sysroot_target_bindir(compiler, host);
let rust_lld = exe("rust-lld", compiler.host); let rust_lld = exe("rust-lld", compiler.host);
builder.copy_link(&src_dir.join(&rust_lld), &dst_dir.join(&rust_lld)); builder.copy_link(
&src_dir.join(&rust_lld),
&dst_dir.join(&rust_lld),
FileType::Executable,
);
let self_contained_lld_src_dir = src_dir.join("gcc-ld"); let self_contained_lld_src_dir = src_dir.join("gcc-ld");
let self_contained_lld_dst_dir = dst_dir.join("gcc-ld"); let self_contained_lld_dst_dir = dst_dir.join("gcc-ld");
t!(fs::create_dir(&self_contained_lld_dst_dir)); t!(fs::create_dir(&self_contained_lld_dst_dir));
@ -472,6 +477,7 @@ impl Step for Rustc {
builder.copy_link( builder.copy_link(
&self_contained_lld_src_dir.join(&exe_name), &self_contained_lld_src_dir.join(&exe_name),
&self_contained_lld_dst_dir.join(&exe_name), &self_contained_lld_dst_dir.join(&exe_name),
FileType::Executable,
); );
} }
} }
@ -480,13 +486,17 @@ impl Step for Rustc {
let src_dir = builder.sysroot_target_bindir(compiler, host); let src_dir = builder.sysroot_target_bindir(compiler, host);
let llvm_objcopy = exe("llvm-objcopy", compiler.host); let llvm_objcopy = exe("llvm-objcopy", compiler.host);
let rust_objcopy = exe("rust-objcopy", compiler.host); let rust_objcopy = exe("rust-objcopy", compiler.host);
builder.copy_link(&src_dir.join(&llvm_objcopy), &dst_dir.join(&rust_objcopy)); builder.copy_link(
&src_dir.join(&llvm_objcopy),
&dst_dir.join(&rust_objcopy),
FileType::Executable,
);
} }
if builder.tool_enabled("wasm-component-ld") { if builder.tool_enabled("wasm-component-ld") {
let src_dir = builder.sysroot_target_bindir(compiler, host); let src_dir = builder.sysroot_target_bindir(compiler, host);
let ld = exe("wasm-component-ld", compiler.host); let ld = exe("wasm-component-ld", compiler.host);
builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld)); builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld), FileType::Executable);
} }
// Man pages // Man pages
@ -511,15 +521,19 @@ impl Step for Rustc {
// HTML copyright files // HTML copyright files
let file_list = builder.ensure(super::run::GenerateCopyright); let file_list = builder.ensure(super::run::GenerateCopyright);
for file in file_list { for file in file_list {
builder.install(&file, &image.join("share/doc/rust"), 0o644); builder.install(&file, &image.join("share/doc/rust"), FileType::Regular);
} }
// README // README
builder.install(&builder.src.join("README.md"), &image.join("share/doc/rust"), 0o644); builder.install(
&builder.src.join("README.md"),
&image.join("share/doc/rust"),
FileType::Regular,
);
// The REUSE-managed license files // The REUSE-managed license files
let license = |path: &Path| { let license = |path: &Path| {
builder.install(path, &image.join("share/doc/rust/licenses"), 0o644); builder.install(path, &image.join("share/doc/rust/licenses"), FileType::Regular);
}; };
for entry in t!(std::fs::read_dir(builder.src.join("LICENSES"))).flatten() { for entry in t!(std::fs::read_dir(builder.src.join("LICENSES"))).flatten() {
license(&entry.path()); license(&entry.path());
@ -548,14 +562,14 @@ impl Step for DebuggerScripts {
let dst = sysroot.join("lib/rustlib/etc"); let dst = sysroot.join("lib/rustlib/etc");
t!(fs::create_dir_all(&dst)); t!(fs::create_dir_all(&dst));
let cp_debugger_script = |file: &str| { let cp_debugger_script = |file: &str| {
builder.install(&builder.src.join("src/etc/").join(file), &dst, 0o644); builder.install(&builder.src.join("src/etc/").join(file), &dst, FileType::Regular);
}; };
if host.contains("windows-msvc") { if host.contains("windows-msvc") {
// windbg debugger scripts // windbg debugger scripts
builder.install( builder.install(
&builder.src.join("src/etc/rust-windbg.cmd"), &builder.src.join("src/etc/rust-windbg.cmd"),
&sysroot.join("bin"), &sysroot.join("bin"),
0o755, FileType::Script,
); );
cp_debugger_script("natvis/intrinsic.natvis"); cp_debugger_script("natvis/intrinsic.natvis");
@ -567,15 +581,27 @@ impl Step for DebuggerScripts {
cp_debugger_script("rust_types.py"); cp_debugger_script("rust_types.py");
// gdb debugger scripts // gdb debugger scripts
builder.install(&builder.src.join("src/etc/rust-gdb"), &sysroot.join("bin"), 0o755); builder.install(
builder.install(&builder.src.join("src/etc/rust-gdbgui"), &sysroot.join("bin"), 0o755); &builder.src.join("src/etc/rust-gdb"),
&sysroot.join("bin"),
FileType::Script,
);
builder.install(
&builder.src.join("src/etc/rust-gdbgui"),
&sysroot.join("bin"),
FileType::Script,
);
cp_debugger_script("gdb_load_rust_pretty_printers.py"); cp_debugger_script("gdb_load_rust_pretty_printers.py");
cp_debugger_script("gdb_lookup.py"); cp_debugger_script("gdb_lookup.py");
cp_debugger_script("gdb_providers.py"); cp_debugger_script("gdb_providers.py");
// lldb debugger scripts // lldb debugger scripts
builder.install(&builder.src.join("src/etc/rust-lldb"), &sysroot.join("bin"), 0o755); builder.install(
&builder.src.join("src/etc/rust-lldb"),
&sysroot.join("bin"),
FileType::Script,
);
cp_debugger_script("lldb_lookup.py"); cp_debugger_script("lldb_lookup.py");
cp_debugger_script("lldb_providers.py"); cp_debugger_script("lldb_providers.py");
@ -640,9 +666,13 @@ fn copy_target_libs(
t!(fs::create_dir_all(&self_contained_dst)); t!(fs::create_dir_all(&self_contained_dst));
for (path, dependency_type) in builder.read_stamp_file(stamp) { for (path, dependency_type) in builder.read_stamp_file(stamp) {
if dependency_type == DependencyType::TargetSelfContained { if dependency_type == DependencyType::TargetSelfContained {
builder.copy_link(&path, &self_contained_dst.join(path.file_name().unwrap())); builder.copy_link(
&path,
&self_contained_dst.join(path.file_name().unwrap()),
FileType::NativeLibrary,
);
} else if dependency_type == DependencyType::Target || builder.is_builder_target(target) { } else if dependency_type == DependencyType::Target || builder.is_builder_target(target) {
builder.copy_link(&path, &dst.join(path.file_name().unwrap())); builder.copy_link(&path, &dst.join(path.file_name().unwrap()), FileType::NativeLibrary);
} }
} }
} }
@ -750,7 +780,11 @@ impl Step for RustcDev {
&tarball.image_dir().join("lib/rustlib/rustc-src/rust"), &tarball.image_dir().join("lib/rustlib/rustc-src/rust"),
); );
for file in src_files { for file in src_files {
tarball.add_file(builder.src.join(file), "lib/rustlib/rustc-src/rust", 0o644); tarball.add_file(
builder.src.join(file),
"lib/rustlib/rustc-src/rust",
FileType::Regular,
);
} }
Some(tarball.generate()) Some(tarball.generate())
@ -1045,7 +1079,11 @@ impl Step for PlainSourceTarball {
// Copy the files normally // Copy the files normally
for item in &src_files { for item in &src_files {
builder.copy_link(&builder.src.join(item), &plain_dst_src.join(item)); builder.copy_link(
&builder.src.join(item),
&plain_dst_src.join(item),
FileType::Regular,
);
} }
// Create the version file // Create the version file
@ -1147,9 +1185,14 @@ impl Step for Cargo {
let mut tarball = Tarball::new(builder, "cargo", &target.triple); let mut tarball = Tarball::new(builder, "cargo", &target.triple);
tarball.set_overlay(OverlayKind::Cargo); tarball.set_overlay(OverlayKind::Cargo);
tarball.add_file(cargo.tool_path, "bin", 0o755); tarball.add_file(&cargo.tool_path, "bin", FileType::Executable);
tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", 0o644); tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", FileType::Regular);
tarball.add_renamed_file(etc.join("cargo.bashcomp.sh"), "etc/bash_completion.d", "cargo"); tarball.add_renamed_file(
etc.join("cargo.bashcomp.sh"),
"etc/bash_completion.d",
"cargo",
FileType::Regular,
);
tarball.add_dir(etc.join("man"), "share/man/man1"); tarball.add_dir(etc.join("man"), "share/man/man1");
tarball.add_legal_and_readme_to("share/doc/cargo"); tarball.add_legal_and_readme_to("share/doc/cargo");
@ -1193,7 +1236,7 @@ impl Step for RustAnalyzer {
let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple); let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple);
tarball.set_overlay(OverlayKind::RustAnalyzer); tarball.set_overlay(OverlayKind::RustAnalyzer);
tarball.is_preview(true); tarball.is_preview(true);
tarball.add_file(rust_analyzer.tool_path, "bin", 0o755); tarball.add_file(&rust_analyzer.tool_path, "bin", FileType::Executable);
tarball.add_legal_and_readme_to("share/doc/rust-analyzer"); tarball.add_legal_and_readme_to("share/doc/rust-analyzer");
Some(tarball.generate()) Some(tarball.generate())
} }
@ -1239,8 +1282,8 @@ impl Step for Clippy {
let mut tarball = Tarball::new(builder, "clippy", &target.triple); let mut tarball = Tarball::new(builder, "clippy", &target.triple);
tarball.set_overlay(OverlayKind::Clippy); tarball.set_overlay(OverlayKind::Clippy);
tarball.is_preview(true); tarball.is_preview(true);
tarball.add_file(clippy.tool_path, "bin", 0o755); tarball.add_file(&clippy.tool_path, "bin", FileType::Executable);
tarball.add_file(cargoclippy.tool_path, "bin", 0o755); tarball.add_file(&cargoclippy.tool_path, "bin", FileType::Executable);
tarball.add_legal_and_readme_to("share/doc/clippy"); tarball.add_legal_and_readme_to("share/doc/clippy");
Some(tarball.generate()) Some(tarball.generate())
} }
@ -1289,8 +1332,8 @@ impl Step for Miri {
let mut tarball = Tarball::new(builder, "miri", &target.triple); let mut tarball = Tarball::new(builder, "miri", &target.triple);
tarball.set_overlay(OverlayKind::Miri); tarball.set_overlay(OverlayKind::Miri);
tarball.is_preview(true); tarball.is_preview(true);
tarball.add_file(miri.tool_path, "bin", 0o755); tarball.add_file(&miri.tool_path, "bin", FileType::Executable);
tarball.add_file(cargomiri.tool_path, "bin", 0o755); tarball.add_file(&cargomiri.tool_path, "bin", FileType::Executable);
tarball.add_legal_and_readme_to("share/doc/miri"); tarball.add_legal_and_readme_to("share/doc/miri");
Some(tarball.generate()) Some(tarball.generate())
} }
@ -1374,7 +1417,11 @@ impl Step for CodegenBackend {
for backend in fs::read_dir(&backends_src).unwrap() { for backend in fs::read_dir(&backends_src).unwrap() {
let file_name = backend.unwrap().file_name(); let file_name = backend.unwrap().file_name();
if file_name.to_str().unwrap().contains(&backend_name) { if file_name.to_str().unwrap().contains(&backend_name) {
tarball.add_file(backends_src.join(file_name), &backends_dst, 0o644); tarball.add_file(
backends_src.join(file_name),
&backends_dst,
FileType::NativeLibrary,
);
found_backend = true; found_backend = true;
} }
} }
@ -1420,8 +1467,8 @@ impl Step for Rustfmt {
let mut tarball = Tarball::new(builder, "rustfmt", &target.triple); let mut tarball = Tarball::new(builder, "rustfmt", &target.triple);
tarball.set_overlay(OverlayKind::Rustfmt); tarball.set_overlay(OverlayKind::Rustfmt);
tarball.is_preview(true); tarball.is_preview(true);
tarball.add_file(rustfmt.tool_path, "bin", 0o755); tarball.add_file(&rustfmt.tool_path, "bin", FileType::Executable);
tarball.add_file(cargofmt.tool_path, "bin", 0o755); tarball.add_file(&cargofmt.tool_path, "bin", FileType::Executable);
tarball.add_legal_and_readme_to("share/doc/rustfmt"); tarball.add_legal_and_readme_to("share/doc/rustfmt");
Some(tarball.generate()) Some(tarball.generate())
} }
@ -1578,27 +1625,33 @@ impl Step for Extended {
&work.join(format!("{}-{}", pkgname(builder, name), target.triple)), &work.join(format!("{}-{}", pkgname(builder, name), target.triple)),
&pkg.join(name), &pkg.join(name),
); );
builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), 0o755); builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), FileType::Script);
pkgbuild(name); pkgbuild(name);
}; };
prepare("rustc"); prepare("rustc");
prepare("cargo"); prepare("cargo");
prepare("rust-std"); prepare("rust-std");
prepare("rust-analysis"); prepare("rust-analysis");
prepare("clippy");
prepare("rust-analyzer"); for tool in &[
for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { "clippy",
"rustfmt",
"rust-analyzer",
"rust-docs",
"miri",
"rustc-codegen-cranelift",
] {
if built_tools.contains(tool) { if built_tools.contains(tool) {
prepare(tool); prepare(tool);
} }
} }
// create an 'uninstall' package // create an 'uninstall' package
builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755); builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), FileType::Script);
pkgbuild("uninstall"); pkgbuild("uninstall");
builder.create_dir(&pkg.join("res")); builder.create_dir(&pkg.join("res"));
builder.create(&pkg.join("res/LICENSE.txt"), &license); builder.create(&pkg.join("res/LICENSE.txt"), &license);
builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644); builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), FileType::Regular);
let mut cmd = command("productbuild"); let mut cmd = command("productbuild");
cmd.arg("--distribution") cmd.arg("--distribution")
.arg(xform(&etc.join("pkg/Distribution.xml"))) .arg(xform(&etc.join("pkg/Distribution.xml")))
@ -1627,6 +1680,8 @@ impl Step for Extended {
"rust-analyzer-preview".to_string() "rust-analyzer-preview".to_string()
} else if name == "clippy" { } else if name == "clippy" {
"clippy-preview".to_string() "clippy-preview".to_string()
} else if name == "rustfmt" {
"rustfmt-preview".to_string()
} else if name == "miri" { } else if name == "miri" {
"miri-preview".to_string() "miri-preview".to_string()
} else if name == "rustc-codegen-cranelift" { } else if name == "rustc-codegen-cranelift" {
@ -1646,7 +1701,7 @@ impl Step for Extended {
prepare("cargo"); prepare("cargo");
prepare("rust-analysis"); prepare("rust-analysis");
prepare("rust-std"); prepare("rust-std");
for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] {
if built_tools.contains(tool) { if built_tools.contains(tool) {
prepare(tool); prepare(tool);
} }
@ -1655,7 +1710,7 @@ impl Step for Extended {
prepare("rust-mingw"); prepare("rust-mingw");
} }
builder.install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644); builder.install(&etc.join("gfx/rust-logo.ico"), &exe, FileType::Regular);
// Generate msi installer // Generate msi installer
let wix_path = env::var_os("WIX") let wix_path = env::var_os("WIX")
@ -1764,6 +1819,24 @@ impl Step for Extended {
.arg(etc.join("msi/remove-duplicates.xsl")) .arg(etc.join("msi/remove-duplicates.xsl"))
.run(builder); .run(builder);
} }
if built_tools.contains("rustfmt") {
command(&heat)
.current_dir(&exe)
.arg("dir")
.arg("rustfmt")
.args(heat_flags)
.arg("-cg")
.arg("RustFmtGroup")
.arg("-dr")
.arg("RustFmt")
.arg("-var")
.arg("var.RustFmtDir")
.arg("-out")
.arg(exe.join("RustFmtGroup.wxs"))
.arg("-t")
.arg(etc.join("msi/remove-duplicates.xsl"))
.run(builder);
}
if built_tools.contains("miri") { if built_tools.contains("miri") {
command(&heat) command(&heat)
.current_dir(&exe) .current_dir(&exe)
@ -1830,11 +1903,14 @@ impl Step for Extended {
.arg("-out") .arg("-out")
.arg(&output) .arg(&output)
.arg(input); .arg(input);
add_env(builder, &mut cmd, target); add_env(builder, &mut cmd, target, &built_tools);
if built_tools.contains("clippy") { if built_tools.contains("clippy") {
cmd.arg("-dClippyDir=clippy"); cmd.arg("-dClippyDir=clippy");
} }
if built_tools.contains("rustfmt") {
cmd.arg("-dRustFmtDir=rustfmt");
}
if built_tools.contains("rust-docs") { if built_tools.contains("rust-docs") {
cmd.arg("-dDocsDir=rust-docs"); cmd.arg("-dDocsDir=rust-docs");
} }
@ -1861,6 +1937,9 @@ impl Step for Extended {
if built_tools.contains("clippy") { if built_tools.contains("clippy") {
candle("ClippyGroup.wxs".as_ref()); candle("ClippyGroup.wxs".as_ref());
} }
if built_tools.contains("rustfmt") {
candle("RustFmtGroup.wxs".as_ref());
}
if built_tools.contains("miri") { if built_tools.contains("miri") {
candle("MiriGroup.wxs".as_ref()); candle("MiriGroup.wxs".as_ref());
} }
@ -1874,8 +1953,8 @@ impl Step for Extended {
} }
builder.create(&exe.join("LICENSE.rtf"), &rtf); builder.create(&exe.join("LICENSE.rtf"), &rtf);
builder.install(&etc.join("gfx/banner.bmp"), &exe, 0o644); builder.install(&etc.join("gfx/banner.bmp"), &exe, FileType::Regular);
builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644); builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, FileType::Regular);
builder.info(&format!("building `msi` installer with {light:?}")); builder.info(&format!("building `msi` installer with {light:?}"));
let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple); let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple);
@ -1899,6 +1978,9 @@ impl Step for Extended {
if built_tools.contains("clippy") { if built_tools.contains("clippy") {
cmd.arg("ClippyGroup.wixobj"); cmd.arg("ClippyGroup.wixobj");
} }
if built_tools.contains("rustfmt") {
cmd.arg("RustFmtGroup.wixobj");
}
if built_tools.contains("miri") { if built_tools.contains("miri") {
cmd.arg("MiriGroup.wixobj"); cmd.arg("MiriGroup.wixobj");
} }
@ -1925,7 +2007,12 @@ impl Step for Extended {
} }
} }
fn add_env(builder: &Builder<'_>, cmd: &mut BootstrapCommand, target: TargetSelection) { fn add_env(
builder: &Builder<'_>,
cmd: &mut BootstrapCommand,
target: TargetSelection,
built_tools: &HashSet<&'static str>,
) {
let mut parts = builder.version.split('.'); let mut parts = builder.version.split('.');
cmd.env("CFG_RELEASE_INFO", builder.rust_version()) cmd.env("CFG_RELEASE_INFO", builder.rust_version())
.env("CFG_RELEASE_NUM", &builder.version) .env("CFG_RELEASE_NUM", &builder.version)
@ -1946,6 +2033,15 @@ fn add_env(builder: &Builder<'_>, cmd: &mut BootstrapCommand, target: TargetSele
} else { } else {
cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC"); cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC");
} }
// ensure these variables are defined
let mut define_optional_tool = |tool_name: &str, env_name: &str| {
cmd.env(env_name, if built_tools.contains(tool_name) { "1" } else { "0" });
};
define_optional_tool("rustfmt", "CFG_RUSTFMT");
define_optional_tool("clippy", "CFG_CLIPPY");
define_optional_tool("miri", "CFG_MIRI");
define_optional_tool("rust-analyzer", "CFG_RA");
} }
fn install_llvm_file( fn install_llvm_file(
@ -1961,13 +2057,13 @@ fn install_llvm_file(
if source.is_symlink() { if source.is_symlink() {
// If we have a symlink like libLLVM-18.so -> libLLVM.so.18.1, install the target of the // If we have a symlink like libLLVM-18.so -> libLLVM.so.18.1, install the target of the
// symlink, which is what will actually get loaded at runtime. // symlink, which is what will actually get loaded at runtime.
builder.install(&t!(fs::canonicalize(source)), destination, 0o644); builder.install(&t!(fs::canonicalize(source)), destination, FileType::NativeLibrary);
let full_dest = destination.join(source.file_name().unwrap()); let full_dest = destination.join(source.file_name().unwrap());
if install_symlink { if install_symlink {
// For download-ci-llvm, also install the symlink, to match what LLVM does. Using a // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a
// symlink is fine here, as this is not a rustup component. // symlink is fine here, as this is not a rustup component.
builder.copy_link(source, &full_dest); builder.copy_link(source, &full_dest, FileType::NativeLibrary);
} else { } else {
// Otherwise, replace the symlink with an equivalent linker script. This is used when // Otherwise, replace the symlink with an equivalent linker script. This is used when
// projects like miri link against librustc_driver.so. We don't use a symlink, as // projects like miri link against librustc_driver.so. We don't use a symlink, as
@ -1984,7 +2080,7 @@ fn install_llvm_file(
} }
} }
} else { } else {
builder.install(source, destination, 0o644); builder.install(source, destination, FileType::NativeLibrary);
} }
} }
@ -2036,7 +2132,7 @@ fn maybe_install_llvm(
let src_libdir = builder.llvm_out(target).join("lib"); let src_libdir = builder.llvm_out(target).join("lib");
let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); let llvm_dylib_path = src_libdir.join("libLLVM.dylib");
if llvm_dylib_path.exists() { if llvm_dylib_path.exists() {
builder.install(&llvm_dylib_path, dst_libdir, 0o644); builder.install(&llvm_dylib_path, dst_libdir, FileType::NativeLibrary);
} }
!builder.config.dry_run() !builder.config.dry_run()
} else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) = } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
@ -2186,7 +2282,7 @@ impl Step for LlvmTools {
let dst_bindir = format!("lib/rustlib/{}/bin", target.triple); let dst_bindir = format!("lib/rustlib/{}/bin", target.triple);
for tool in tools_to_install(&builder.paths) { for tool in tools_to_install(&builder.paths) {
let exe = src_bindir.join(exe(tool, target)); let exe = src_bindir.join(exe(tool, target));
tarball.add_file(&exe, &dst_bindir, 0o755); tarball.add_file(&exe, &dst_bindir, FileType::Executable);
} }
} }
@ -2241,7 +2337,7 @@ impl Step for LlvmBitcodeLinker {
tarball.set_overlay(OverlayKind::LlvmBitcodeLinker); tarball.set_overlay(OverlayKind::LlvmBitcodeLinker);
tarball.is_preview(true); tarball.is_preview(true);
tarball.add_file(llbc_linker.tool_path, self_contained_bin_dir, 0o755); tarball.add_file(&llbc_linker.tool_path, self_contained_bin_dir, FileType::Executable);
Some(tarball.generate()) Some(tarball.generate())
} }
@ -2302,7 +2398,7 @@ impl Step for RustDev {
let entry = t!(entry); let entry = t!(entry);
if entry.file_type().is_file() && !entry.path_is_symlink() { if entry.file_type().is_file() && !entry.path_is_symlink() {
let name = entry.file_name().to_str().unwrap(); let name = entry.file_name().to_str().unwrap();
tarball.add_file(src_bindir.join(name), "bin", 0o755); tarball.add_file(src_bindir.join(name), "bin", FileType::Executable);
} }
} }
} }
@ -2314,11 +2410,11 @@ impl Step for RustDev {
// We don't build LLD on some platforms, so only add it if it exists // We don't build LLD on some platforms, so only add it if it exists
let lld_path = lld_out.join("bin").join(exe("lld", target)); let lld_path = lld_out.join("bin").join(exe("lld", target));
if lld_path.exists() { if lld_path.exists() {
tarball.add_file(lld_path, "bin", 0o755); tarball.add_file(&lld_path, "bin", FileType::Executable);
} }
} }
tarball.add_file(builder.llvm_filecheck(target), "bin", 0o755); tarball.add_file(builder.llvm_filecheck(target), "bin", FileType::Executable);
// Copy the include directory as well; needed mostly to build // Copy the include directory as well; needed mostly to build
// librustc_llvm properly (e.g., llvm-config.h is in here). But also // librustc_llvm properly (e.g., llvm-config.h is in here). But also
@ -2379,7 +2475,11 @@ impl Step for Bootstrap {
let bootstrap_outdir = &builder.bootstrap_out; let bootstrap_outdir = &builder.bootstrap_out;
for file in &["bootstrap", "rustc", "rustdoc"] { for file in &["bootstrap", "rustc", "rustdoc"] {
tarball.add_file(bootstrap_outdir.join(exe(file, target)), "bootstrap/bin", 0o755); tarball.add_file(
bootstrap_outdir.join(exe(file, target)),
"bootstrap/bin",
FileType::Executable,
);
} }
Some(tarball.generate()) Some(tarball.generate())
@ -2412,7 +2512,7 @@ impl Step for BuildManifest {
let build_manifest = builder.tool_exe(Tool::BuildManifest); let build_manifest = builder.tool_exe(Tool::BuildManifest);
let tarball = Tarball::new(builder, "build-manifest", &self.target.triple); let tarball = Tarball::new(builder, "build-manifest", &self.target.triple);
tarball.add_file(build_manifest, "bin", 0o755); tarball.add_file(&build_manifest, "bin", FileType::Executable);
tarball.generate() tarball.generate()
} }
} }
@ -2444,15 +2544,15 @@ impl Step for ReproducibleArtifacts {
let mut added_anything = false; let mut added_anything = false;
let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple); let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple);
if let Some(path) = builder.config.rust_profile_use.as_ref() { if let Some(path) = builder.config.rust_profile_use.as_ref() {
tarball.add_file(path, ".", 0o644); tarball.add_file(path, ".", FileType::Regular);
added_anything = true; added_anything = true;
} }
if let Some(path) = builder.config.llvm_profile_use.as_ref() { if let Some(path) = builder.config.llvm_profile_use.as_ref() {
tarball.add_file(path, ".", 0o644); tarball.add_file(path, ".", FileType::Regular);
added_anything = true; added_anything = true;
} }
for profile in &builder.config.reproducible_artifacts { for profile in &builder.config.reproducible_artifacts {
tarball.add_file(profile, ".", 0o644); tarball.add_file(profile, ".", FileType::Regular);
added_anything = true; added_anything = true;
} }
if added_anything { Some(tarball.generate()) } else { None } if added_anything { Some(tarball.generate()) } else { None }
@ -2481,7 +2581,7 @@ impl Step for Gcc {
fn run(self, builder: &Builder<'_>) -> Self::Output { fn run(self, builder: &Builder<'_>) -> Self::Output {
let tarball = Tarball::new(builder, "gcc", &self.target.triple); let tarball = Tarball::new(builder, "gcc", &self.target.triple);
let output = builder.ensure(super::gcc::Gcc { target: self.target }); let output = builder.ensure(super::gcc::Gcc { target: self.target });
tarball.add_file(output.libgccjit, "lib", 0o644); tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary);
tarball.generate() tarball.generate()
} }
} }

View File

@ -11,7 +11,6 @@ use std::io::{self, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{env, fs, mem}; use std::{env, fs, mem};
use crate::Mode;
use crate::core::build_steps::compile; use crate::core::build_steps::compile;
use crate::core::build_steps::tool::{self, SourceType, Tool, prepare_tool_cargo}; use crate::core::build_steps::tool::{self, SourceType, Tool, prepare_tool_cargo};
use crate::core::builder::{ use crate::core::builder::{
@ -19,6 +18,7 @@ use crate::core::builder::{
}; };
use crate::core::config::{Config, TargetSelection}; use crate::core::config::{Config, TargetSelection};
use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date};
use crate::{FileType, Mode};
macro_rules! book { macro_rules! book {
($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => {
@ -546,6 +546,7 @@ impl Step for SharedAssets {
builder.copy_link( builder.copy_link(
&builder.src.join("src").join("doc").join("rust.css"), &builder.src.join("src").join("doc").join("rust.css"),
&out.join("rust.css"), &out.join("rust.css"),
FileType::Regular,
); );
SharedAssetsPaths { version_info } SharedAssetsPaths { version_info }

View File

@ -26,7 +26,7 @@ use crate::core::config::{DebuginfoLevel, RustcLto, TargetSelection};
use crate::utils::channel::GitInfo; use crate::utils::channel::GitInfo;
use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::exec::{BootstrapCommand, command};
use crate::utils::helpers::{add_dylib_path, exe, t}; use crate::utils::helpers::{add_dylib_path, exe, t};
use crate::{Compiler, Kind, Mode, gha}; use crate::{Compiler, FileType, Kind, Mode, gha};
#[derive(Debug, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum SourceType { pub enum SourceType {
@ -353,7 +353,7 @@ fn copy_link_tool_bin(
) -> PathBuf { ) -> PathBuf {
let cargo_out = builder.cargo_out(compiler, mode, target).join(exe(name, target)); let cargo_out = builder.cargo_out(compiler, mode, target).join(exe(name, target));
let bin = builder.tools_dir(compiler).join(exe(name, target)); let bin = builder.tools_dir(compiler).join(exe(name, target));
builder.copy_link(&cargo_out, &bin); builder.copy_link(&cargo_out, &bin, FileType::Executable);
bin bin
} }
@ -696,7 +696,7 @@ impl Step for Rustdoc {
.join(exe("rustdoc", target_compiler.host)); .join(exe("rustdoc", target_compiler.host));
let bin_rustdoc = bin_rustdoc(); let bin_rustdoc = bin_rustdoc();
builder.copy_link(&precompiled_rustdoc, &bin_rustdoc); builder.copy_link(&precompiled_rustdoc, &bin_rustdoc, FileType::Executable);
return ToolBuildResult { return ToolBuildResult {
tool_path: bin_rustdoc, tool_path: bin_rustdoc,
@ -743,7 +743,7 @@ impl Step for Rustdoc {
compile::strip_debug(builder, target, &tool_path); compile::strip_debug(builder, target, &tool_path);
} }
let bin_rustdoc = bin_rustdoc(); let bin_rustdoc = bin_rustdoc();
builder.copy_link(&tool_path, &bin_rustdoc); builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable);
ToolBuildResult { tool_path: bin_rustdoc, build_compiler, target_compiler } ToolBuildResult { tool_path: bin_rustdoc, build_compiler, target_compiler }
} else { } else {
ToolBuildResult { tool_path, build_compiler, target_compiler } ToolBuildResult { tool_path, build_compiler, target_compiler }
@ -846,13 +846,20 @@ impl Step for LldWrapper {
let src_exe = exe("lld", target); let src_exe = exe("lld", target);
let dst_exe = exe("rust-lld", target); let dst_exe = exe("rust-lld", target);
builder.copy_link(&lld_install.join("bin").join(src_exe), &libdir_bin.join(dst_exe)); builder.copy_link(
&lld_install.join("bin").join(src_exe),
&libdir_bin.join(dst_exe),
FileType::Executable,
);
let self_contained_lld_dir = libdir_bin.join("gcc-ld"); let self_contained_lld_dir = libdir_bin.join("gcc-ld");
t!(fs::create_dir_all(&self_contained_lld_dir)); t!(fs::create_dir_all(&self_contained_lld_dir));
for name in crate::LLD_FILE_NAMES { for name in crate::LLD_FILE_NAMES {
builder builder.copy_link(
.copy_link(&tool_result.tool_path, &self_contained_lld_dir.join(exe(name, target))); &tool_result.tool_path,
&self_contained_lld_dir.join(exe(name, target)),
FileType::Executable,
);
} }
tool_result tool_result
@ -949,8 +956,11 @@ impl Step for RustAnalyzerProcMacroSrv {
// so that r-a can use it. // so that r-a can use it.
let libexec_path = builder.sysroot(self.compiler).join("libexec"); let libexec_path = builder.sysroot(self.compiler).join("libexec");
t!(fs::create_dir_all(&libexec_path)); t!(fs::create_dir_all(&libexec_path));
builder builder.copy_link(
.copy_link(&tool_result.tool_path, &libexec_path.join("rust-analyzer-proc-macro-srv")); &tool_result.tool_path,
&libexec_path.join("rust-analyzer-proc-macro-srv"),
FileType::Executable,
);
Some(tool_result) Some(tool_result)
} }
@ -1007,7 +1017,7 @@ impl Step for LlvmBitcodeLinker {
t!(fs::create_dir_all(&bindir_self_contained)); t!(fs::create_dir_all(&bindir_self_contained));
let bin_destination = bindir_self_contained let bin_destination = bindir_self_contained
.join(exe("llvm-bitcode-linker", tool_result.target_compiler.host)); .join(exe("llvm-bitcode-linker", tool_result.target_compiler.host));
builder.copy_link(&tool_result.tool_path, &bin_destination); builder.copy_link(&tool_result.tool_path, &bin_destination, FileType::Executable);
ToolBuildResult { ToolBuildResult {
tool_path: bin_destination, tool_path: bin_destination,
build_compiler: tool_result.build_compiler, build_compiler: tool_result.build_compiler,
@ -1189,7 +1199,7 @@ fn run_tool_build_step(
for add_bin in add_bins_to_sysroot { for add_bin in add_bins_to_sysroot {
let bin_destination = bindir.join(exe(add_bin, target_compiler.host)); let bin_destination = bindir.join(exe(add_bin, target_compiler.host));
builder.copy_link(&tool_path, &bin_destination); builder.copy_link(&tool_path, &bin_destination, FileType::Executable);
} }
// Return a path into the bin dir. // Return a path into the bin dir.

View File

@ -37,7 +37,9 @@ use crate::core::builder;
use crate::core::builder::Kind; use crate::core::builder::Kind;
use crate::core::config::{DryRun, LldMode, LlvmLibunwind, Target, TargetSelection, flags}; use crate::core::config::{DryRun, LldMode, LlvmLibunwind, Target, TargetSelection, flags};
use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command}; use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command};
use crate::utils::helpers::{self, dir_is_empty, exe, libdir, output, set_file_times, symlink_dir}; use crate::utils::helpers::{
self, dir_is_empty, exe, libdir, output, set_file_times, split_debuginfo, symlink_dir,
};
mod core; mod core;
mod utils; mod utils;
@ -275,6 +277,35 @@ pub enum CLang {
Cxx, Cxx,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileType {
/// An executable binary file (like a `.exe`).
Executable,
/// A native, binary library file (like a `.so`, `.dll`, `.a`, `.lib` or `.o`).
NativeLibrary,
/// An executable (non-binary) script file (like a `.py` or `.sh`).
Script,
/// Any other regular file that is non-executable.
Regular,
}
impl FileType {
/// Get Unix permissions appropriate for this file type.
pub fn perms(self) -> u32 {
match self {
FileType::Executable | FileType::Script => 0o755,
FileType::Regular | FileType::NativeLibrary => 0o644,
}
}
pub fn could_have_split_debuginfo(self) -> bool {
match self {
FileType::Executable | FileType::NativeLibrary => true,
FileType::Script | FileType::Regular => false,
}
}
}
macro_rules! forward { macro_rules! forward {
( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => { ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => {
impl Build { impl Build {
@ -1745,8 +1776,18 @@ Executed at: {executed_at}"#,
/// Attempts to use hard links if possible, falling back to copying. /// Attempts to use hard links if possible, falling back to copying.
/// You can neither rely on this being a copy nor it being a link, /// You can neither rely on this being a copy nor it being a link,
/// so do not write to dst. /// so do not write to dst.
pub fn copy_link(&self, src: &Path, dst: &Path) { pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {
self.copy_link_internal(src, dst, false); self.copy_link_internal(src, dst, false);
if file_type.could_have_split_debuginfo() {
if let Some(dbg_file) = split_debuginfo(src) {
self.copy_link_internal(
&dbg_file,
&dst.with_extension(dbg_file.extension().unwrap()),
false,
);
}
}
} }
fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) { fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {
@ -1809,7 +1850,7 @@ Executed at: {executed_at}"#,
t!(fs::create_dir_all(&dst)); t!(fs::create_dir_all(&dst));
self.cp_link_r(&path, &dst); self.cp_link_r(&path, &dst);
} else { } else {
self.copy_link(&path, &dst); self.copy_link(&path, &dst, FileType::Regular);
} }
} }
} }
@ -1845,7 +1886,7 @@ Executed at: {executed_at}"#,
self.cp_link_filtered_recurse(&path, &dst, &relative, filter); self.cp_link_filtered_recurse(&path, &dst, &relative, filter);
} else { } else {
let _ = fs::remove_file(&dst); let _ = fs::remove_file(&dst);
self.copy_link(&path, &dst); self.copy_link(&path, &dst, FileType::Regular);
} }
} }
} }
@ -1854,10 +1895,10 @@ Executed at: {executed_at}"#,
fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) { fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) {
let file_name = src.file_name().unwrap(); let file_name = src.file_name().unwrap();
let dest = dest_folder.join(file_name); let dest = dest_folder.join(file_name);
self.copy_link(src, &dest); self.copy_link(src, &dest, FileType::Regular);
} }
fn install(&self, src: &Path, dstdir: &Path, perms: u32) { fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) {
if self.config.dry_run() { if self.config.dry_run() {
return; return;
} }
@ -1867,8 +1908,16 @@ Executed at: {executed_at}"#,
if !src.exists() { if !src.exists() {
panic!("ERROR: File \"{}\" not found!", src.display()); panic!("ERROR: File \"{}\" not found!", src.display());
} }
self.copy_link_internal(src, &dst, true); self.copy_link_internal(src, &dst, true);
chmod(&dst, perms); chmod(&dst, file_type.perms());
// If this file can have debuginfo, look for split debuginfo and install it too.
if file_type.could_have_split_debuginfo() {
if let Some(dbg_file) = split_debuginfo(src) {
self.install(&dbg_file, dstdir, FileType::Regular);
}
}
} }
fn read(&self, path: &Path) -> String { fn read(&self, path: &Path) -> String {

View File

@ -52,6 +52,23 @@ pub fn exe(name: &str, target: TargetSelection) -> String {
crate::utils::shared_helpers::exe(name, &target.triple) crate::utils::shared_helpers::exe(name, &target.triple)
} }
/// Returns the path to the split debug info for the specified file if it exists.
pub fn split_debuginfo(name: impl Into<PathBuf>) -> Option<PathBuf> {
// FIXME: only msvc is currently supported
let path = name.into();
let pdb = path.with_extension("pdb");
if pdb.exists() {
return Some(pdb);
}
// pdbs get named with '-' replaced by '_'
let file_name = pdb.file_name()?.to_str()?.replace("-", "_");
let pdb: PathBuf = [path.parent()?, Path::new(&file_name)].into_iter().collect();
pdb.exists().then_some(pdb)
}
/// Returns `true` if the file name given looks like a dynamic library. /// Returns `true` if the file name given looks like a dynamic library.
pub fn is_dylib(path: &Path) -> bool { pub fn is_dylib(path: &Path) -> bool {
path.extension().and_then(|ext| ext.to_str()).is_some_and(|ext| { path.extension().and_then(|ext| ext.to_str()).is_some_and(|ext| {

View File

@ -7,6 +7,7 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use crate::FileType;
use crate::core::build_steps::dist::distdir; use crate::core::build_steps::dist::distdir;
use crate::core::builder::{Builder, Kind}; use crate::core::builder::{Builder, Kind};
use crate::core::config::BUILDER_CONFIG_FILENAME; use crate::core::config::BUILDER_CONFIG_FILENAME;
@ -182,7 +183,12 @@ impl<'a> Tarball<'a> {
&self.image_dir &self.image_dir
} }
pub(crate) fn add_file(&self, src: impl AsRef<Path>, destdir: impl AsRef<Path>, perms: u32) { pub(crate) fn add_file(
&self,
src: impl AsRef<Path>,
destdir: impl AsRef<Path>,
file_type: FileType,
) {
// create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply // create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply
// uses the base directory as the destination directory. // uses the base directory as the destination directory.
let destdir = if destdir.as_ref() == Path::new(".") { let destdir = if destdir.as_ref() == Path::new(".") {
@ -192,7 +198,7 @@ impl<'a> Tarball<'a> {
}; };
t!(std::fs::create_dir_all(&destdir)); t!(std::fs::create_dir_all(&destdir));
self.builder.install(src.as_ref(), &destdir, perms); self.builder.install(src.as_ref(), &destdir, file_type);
} }
pub(crate) fn add_renamed_file( pub(crate) fn add_renamed_file(
@ -200,15 +206,16 @@ impl<'a> Tarball<'a> {
src: impl AsRef<Path>, src: impl AsRef<Path>,
destdir: impl AsRef<Path>, destdir: impl AsRef<Path>,
new_name: &str, new_name: &str,
file_type: FileType,
) { ) {
let destdir = self.image_dir.join(destdir.as_ref()); let destdir = self.image_dir.join(destdir.as_ref());
t!(std::fs::create_dir_all(&destdir)); t!(std::fs::create_dir_all(&destdir));
self.builder.copy_link(src.as_ref(), &destdir.join(new_name)); self.builder.copy_link(src.as_ref(), &destdir.join(new_name), file_type);
} }
pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef<Path>) { pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef<Path>) {
for file in self.overlay.legal_and_readme() { for file in self.overlay.legal_and_readme() {
self.add_file(self.builder.src.join(file), destdir.as_ref(), 0o644); self.add_file(self.builder.src.join(file), destdir.as_ref(), FileType::Regular);
} }
} }
@ -318,11 +325,20 @@ impl<'a> Tarball<'a> {
// Add config file if present. // Add config file if present.
if let Some(config) = &self.builder.config.config { if let Some(config) = &self.builder.config.config {
self.add_renamed_file(config, &self.overlay_dir, BUILDER_CONFIG_FILENAME); self.add_renamed_file(
config,
&self.overlay_dir,
BUILDER_CONFIG_FILENAME,
FileType::Regular,
);
} }
for file in self.overlay.legal_and_readme() { for file in self.overlay.legal_and_readme() {
self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644); self.builder.install(
&self.builder.src.join(file),
&self.overlay_dir,
FileType::Regular,
);
} }
let mut cmd = self.builder.tool_cmd(crate::core::build_steps::tool::Tool::RustInstaller); let mut cmd = self.builder.tool_cmd(crate::core::build_steps::tool::Tool::RustInstaller);

View File

@ -140,6 +140,7 @@ pub fn get_closest_merge_commit(
// cd \"/checkout\" && \"git\" \"merge-base\" \"origin/master\" \"HEAD\"\nexpected success, got: exit status: 1\n" // cd \"/checkout\" && \"git\" \"merge-base\" \"origin/master\" \"HEAD\"\nexpected success, got: exit status: 1\n"
// ``` // ```
// Investigate and resolve this issue instead of skipping it like this. // Investigate and resolve this issue instead of skipping it like this.
// NOTE (2025-03): this is probably caused by CI using a sparse checkout.
(channel == "nightly" || !CiEnv::is_rust_lang_managed_ci_job()) (channel == "nightly" || !CiEnv::is_rust_lang_managed_ci_job())
{ {
git_upstream_merge_base(config, git_dir).unwrap() git_upstream_merge_base(config, git_dir).unwrap()
@ -150,11 +151,18 @@ pub fn get_closest_merge_commit(
} }
}; };
// Now that rust-lang/rust is the only repo using bors, we can search the entire
// history for a bors commit, not just "first parents". This is crucial to make
// this logic work when the user has currently checked out a subtree sync branch.
// At the same time, we use this logic in CI where only a tiny part of the history
// is even checked out, making this kind of history search very fragile. It turns
// out that by adding `--diff-merges=first-parent`, we get a usable reply
// even for sparse checkouts: it will just return the most recent bors commit.
git.args([ git.args([
"rev-list", "rev-list",
&format!("--author={}", config.git_merge_commit_email), &format!("--author={}", config.git_merge_commit_email),
"-n1", "-n1",
"--first-parent", "--diff-merges=first-parent",
&merge_base, &merge_base,
]); ]);

View File

@ -172,6 +172,19 @@
<!-- tool-rust-docs-end --> <!-- tool-rust-docs-end -->
<Directory Id="Cargo" Name="." /> <Directory Id="Cargo" Name="." />
<Directory Id="Std" Name="." /> <Directory Id="Std" Name="." />
<?if $(env.CFG_RUSTFMT)="1" ?>
<Directory Id="RustFmt" Name="." />
<?endif?>
<?if $(env.CFG_RA)="1" ?>
<Directory Id="RustAnalyzer" Name="." />
<?endif?>
<?if $(env.CFG_MIRI)="1" ?>
<Directory Id="Miri" Name="." />
<?endif?>
<Directory Id="Analysis" Name="." />
<?if $(env.CFG_CLIPPY)="1" ?>
<Directory Id="Clippy" Name="." />
<?endif?>
</Directory> </Directory>
</Directory> </Directory>
@ -279,7 +292,49 @@
<ComponentRef Id="PathEnvPerMachine" /> <ComponentRef Id="PathEnvPerMachine" />
<ComponentRef Id="PathEnvPerUser" /> <ComponentRef Id="PathEnvPerUser" />
</Feature> </Feature>
<?if $(env.CFG_RUSTFMT)="1" ?>
<Feature Id="RustFmt"
Title="Formatter for rust"
Display="7"
Level="1"
AllowAdvertise="no">
<ComponentGroupRef Id="RustFmtGroup" />
</Feature>
<?endif?>
<?if $(env.CFG_CLIPPY)="1" ?>
<Feature Id="Clippy"
Title="Formatter and checker for rust"
Display="8"
Level="1"
AllowAdvertise="no">
<ComponentGroupRef Id="ClippyGroup" />
</Feature>
<?endif?>
<?if $(env.CFG_MIRI)="1" ?>
<Feature Id="Miri"
Title="Soundness checker for rust"
Display="9"
Level="1"
AllowAdvertise="no">
<ComponentGroupRef Id="MiriGroup" />
</Feature>
<?endif?>
<?if $(env.CFG_RA)="1" ?>
<Feature Id="RustAnalyzer"
Title="Analyzer for rust"
Display="10"
Level="1"
AllowAdvertise="no">
<ComponentGroupRef Id="RustAnalyzerGroup" />
</Feature>
<?endif?>
<Feature Id="Analysis"
Title="Analysis for rust"
Display="11"
Level="1"
AllowAdvertise="no">
<ComponentGroupRef Id="AnalysisGroup" />
</Feature>
<UIRef Id="RustUI" /> <UIRef Id="RustUI" />
</Product> </Product>
</Wix> </Wix>

View File

@ -1552,6 +1552,10 @@ impl Type {
matches!(self, Type::BorrowedRef { .. }) matches!(self, Type::BorrowedRef { .. })
} }
fn is_type_alias(&self) -> bool {
matches!(self, Type::Path { path: Path { res: Res::Def(DefKind::TyAlias, _), .. } })
}
/// Check if two types are "the same" for documentation purposes. /// Check if two types are "the same" for documentation purposes.
/// ///
/// This is different from `Eq`, because it knows that things like /// This is different from `Eq`, because it knows that things like
@ -1580,6 +1584,16 @@ impl Type {
} else { } else {
(self, other) (self, other)
}; };
// FIXME: `Cache` does not have the data required to unwrap type aliases,
// so we just assume they are equal.
// This is only remotely acceptable because we were previously
// assuming all types were equal when used
// as a generic parameter of a type in `Deref::Target`.
if self_cleared.is_type_alias() || other_cleared.is_type_alias() {
return true;
}
match (self_cleared, other_cleared) { match (self_cleared, other_cleared) {
// Recursive cases. // Recursive cases.
(Type::Tuple(a), Type::Tuple(b)) => { (Type::Tuple(a), Type::Tuple(b)) => {

View File

@ -89,7 +89,7 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
/// Specifies whether rendering directly implemented trait items or ones from a certain Deref /// Specifies whether rendering directly implemented trait items or ones from a certain Deref
/// impl. /// impl.
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
pub(crate) enum AssocItemRender<'a> { pub(crate) enum AssocItemRender<'a> {
All, All,
DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool }, DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
@ -1296,7 +1296,8 @@ fn render_assoc_items_inner(
info!("Documenting associated items of {:?}", containing_item.name); info!("Documenting associated items of {:?}", containing_item.name);
let cache = &cx.shared.cache; let cache = &cx.shared.cache;
let Some(v) = cache.impls.get(&it) else { return }; let Some(v) = cache.impls.get(&it) else { return };
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); let (mut non_trait, traits): (Vec<_>, _) =
v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() { if !non_trait.is_empty() {
let mut close_tags = <Vec<&str>>::with_capacity(1); let mut close_tags = <Vec<&str>>::with_capacity(1);
let mut tmp_buf = String::new(); let mut tmp_buf = String::new();
@ -1314,6 +1315,16 @@ fn render_assoc_items_inner(
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
let id = let id =
cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
// the `impls.get` above only looks at the outermost type,
// and the Deref impl may only be implemented for certain
// values of generic parameters.
// for example, if an item impls `Deref<[u8]>`,
// we should not show methods from `[MaybeUninit<u8>]`.
// this `retain` filters out any instances where
// the types do not line up perfectly.
non_trait.retain(|impl_| {
type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
});
let derived_id = cx.derive_id(&id); let derived_id = cx.derive_id(&id);
close_tags.push("</details>"); close_tags.push("</details>");
write_str( write_str(
@ -1392,6 +1403,7 @@ fn render_assoc_items_inner(
} }
} }
/// `derefs` is the set of all deref targets that have already been handled.
fn render_deref_methods( fn render_deref_methods(
mut w: impl Write, mut w: impl Write,
cx: &Context<'_>, cx: &Context<'_>,

View File

@ -533,7 +533,10 @@ fn sidebar_deref_methods<'a>(
debug!("found inner_impl: {impls:?}"); debug!("found inner_impl: {impls:?}");
let mut ret = impls let mut ret = impls
.iter() .iter()
.filter(|i| i.inner_impl().trait_.is_none()) .filter(|i| {
i.inner_impl().trait_.is_none()
&& real_target.is_doc_subtype_of(&i.inner_impl().for_, &c)
})
.flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if !ret.is_empty() { if !ret.is_empty() {

View File

@ -0,0 +1,70 @@
// MIR for `demo_ge_partial` after PreCodegen
fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool {
debug a => _1;
debug b => _2;
let mut _0: bool;
scope 1 (inlined std::cmp::impls::<impl PartialOrd for &(f32, f32)>::ge) {
scope 2 (inlined core::tuple::<impl PartialOrd for (f32, f32)>::ge) {
let mut _7: std::ops::ControlFlow<bool>;
let _8: bool;
scope 3 {
}
scope 4 (inlined std::cmp::impls::<impl PartialOrd for f32>::__chaining_ge) {
let mut _3: f32;
let mut _4: f32;
let mut _5: bool;
let mut _6: bool;
scope 5 {
}
}
scope 6 (inlined std::cmp::impls::<impl PartialOrd for f32>::ge) {
let mut _9: f32;
let mut _10: f32;
}
}
}
bb0: {
StorageLive(_7);
StorageLive(_3);
StorageLive(_4);
_3 = copy ((*_1).0: f32);
_4 = copy ((*_2).0: f32);
StorageLive(_5);
_5 = Eq(copy _3, copy _4);
switchInt(move _5) -> [0: bb1, otherwise: bb2];
}
bb1: {
StorageLive(_6);
_6 = Ge(copy _3, copy _4);
_7 = ControlFlow::<bool>::Break(move _6);
StorageDead(_6);
StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
_8 = copy ((_7 as Break).0: bool);
_0 = copy _8;
goto -> bb3;
}
bb2: {
StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
StorageLive(_9);
_9 = copy ((*_1).1: f32);
StorageLive(_10);
_10 = copy ((*_2).1: f32);
_0 = Ge(move _9, move _10);
StorageDead(_10);
StorageDead(_9);
goto -> bb3;
}
bb3: {
StorageDead(_7);
return;
}
}

View File

@ -0,0 +1,70 @@
// MIR for `demo_le_total` after PreCodegen
fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool {
debug a => _1;
debug b => _2;
let mut _0: bool;
scope 1 (inlined std::cmp::impls::<impl PartialOrd for &(u16, i16)>::le) {
scope 2 (inlined core::tuple::<impl PartialOrd for (u16, i16)>::le) {
let mut _7: std::ops::ControlFlow<bool>;
let _8: bool;
scope 3 {
}
scope 4 (inlined std::cmp::impls::<impl PartialOrd for u16>::__chaining_le) {
let mut _3: u16;
let mut _4: u16;
let mut _5: bool;
let mut _6: bool;
scope 5 {
}
}
scope 6 (inlined std::cmp::impls::<impl PartialOrd for i16>::le) {
let mut _9: i16;
let mut _10: i16;
}
}
}
bb0: {
StorageLive(_7);
StorageLive(_3);
StorageLive(_4);
_3 = copy ((*_1).0: u16);
_4 = copy ((*_2).0: u16);
StorageLive(_5);
_5 = Eq(copy _3, copy _4);
switchInt(move _5) -> [0: bb1, otherwise: bb2];
}
bb1: {
StorageLive(_6);
_6 = Le(copy _3, copy _4);
_7 = ControlFlow::<bool>::Break(move _6);
StorageDead(_6);
StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
_8 = copy ((_7 as Break).0: bool);
_0 = copy _8;
goto -> bb3;
}
bb2: {
StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
StorageLive(_9);
_9 = copy ((*_1).1: i16);
StorageLive(_10);
_10 = copy ((*_2).1: i16);
_0 = Le(move _9, move _10);
StorageDead(_10);
StorageDead(_9);
goto -> bb3;
}
bb3: {
StorageDead(_7);
return;
}
}

View File

@ -0,0 +1,16 @@
//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0
//@ needs-unwind
#![crate_type = "lib"]
// EMIT_MIR tuple_ord.demo_le_total.PreCodegen.after.mir
pub fn demo_le_total(a: &(u16, i16), b: &(u16, i16)) -> bool {
// CHECK-LABEL: demo_le_total
a <= b
}
// EMIT_MIR tuple_ord.demo_ge_partial.PreCodegen.after.mir
pub fn demo_ge_partial(a: &(f32, f32), b: &(f32, f32)) -> bool {
// CHECK-LABEL: demo_ge_partial
a >= b
}

View File

@ -0,0 +1,27 @@
#![crate_name = "foo"]
// test for https://github.com/rust-lang/rust/issues/24686
use std::ops::Deref;
pub struct Foo<T>(T);
impl Foo<i32> {
pub fn get_i32(&self) -> i32 { self.0 }
}
impl Foo<u32> {
pub fn get_u32(&self) -> u32 { self.0 }
}
// Note that the same href is used both on the method itself,
// and on the sidebar items.
//@ has foo/struct.Bar.html
//@ has - '//a[@href="#method.get_i32"]' 'get_i32'
//@ !has - '//a[@href="#method.get_u32"]' 'get_u32'
//@ count - '//ul[@class="block deref-methods"]//a' 1
//@ count - '//a[@href="#method.get_i32"]' 2
pub struct Bar(Foo<i32>);
impl Deref for Bar {
type Target = Foo<i32>;
fn deref(&self) -> &Foo<i32> {
&self.0
}
}