Auto merge of #150810 - matthiaskrgr:rollup-fXh6V3q, r=matthiaskrgr

Rollup of 10 pull requests

Successful merges:

 - rust-lang/rust#149976 (Add waker_fn and local_waker_fn to std::task)
 - rust-lang/rust#150074 (Update provider API docs)
 - rust-lang/rust#150094 (`c_variadic`: provide our own `va_arg` implementation for more targets)
 - rust-lang/rust#150164 (rustc: Fix `-Zexport-executable-symbols` on wasm)
 - rust-lang/rust#150569 (Ensure that static initializers are acyclic for NVPTX)
 - rust-lang/rust#150694 (./x check miri: enable check_only feature)
 - rust-lang/rust#150717 (Thread `--jobs` from `bootstrap` -> `compiletest` -> `run-make-support`)
 - rust-lang/rust#150736 (Add AtomicPtr::null)
 - rust-lang/rust#150787 (Add myself as co-maintainer for s390x-unknown-linux-musl)
 - rust-lang/rust#150789 (Fix copy-n-paste error in `vtable_for` docs)

r? @ghost
This commit is contained in:
rust-bors[bot] 2026-01-08 16:24:54 +00:00 committed by GitHub
commit 4586feb998
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 537 additions and 37 deletions

View file

@ -4421,6 +4421,7 @@ dependencies = [
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_middle",
"rustc_session",

View file

@ -1,12 +1,13 @@
use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size, TyAndLayout};
use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size};
use rustc_codegen_ssa::MemFlags;
use rustc_codegen_ssa::common::IntPredicate;
use rustc_codegen_ssa::mir::operand::OperandRef;
use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods,
};
use rustc_middle::bug;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_target::spec::{Abi, Arch, Env};
use crate::builder::Builder;
@ -82,6 +83,7 @@ enum PassMode {
enum SlotSize {
Bytes8 = 8,
Bytes4 = 4,
Bytes1 = 1,
}
enum AllowHigherAlign {
@ -728,7 +730,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
reg_addr: &'ll Value,
layout: TyAndLayout<'tcx, Ty<'tcx>>,
layout: TyAndLayout<'tcx>,
src_align: Align,
) -> &'ll Value {
if layout.layout.align.abi > src_align {
@ -751,7 +753,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
va_list_addr: &'ll Value,
layout: TyAndLayout<'tcx, Ty<'tcx>>,
layout: TyAndLayout<'tcx>,
) -> &'ll Value {
let dl = bx.cx.data_layout();
let ptr_align_abi = dl.data_layout().pointer_align().abi;
@ -1003,15 +1005,17 @@ fn emit_xtensa_va_arg<'ll, 'tcx>(
return bx.load(layout.llvm_type(bx), value_ptr, layout.align.abi);
}
/// Determine the va_arg implementation to use. The LLVM va_arg instruction
/// is lacking in some instances, so we should only use it as a fallback.
pub(super) fn emit_va_arg<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
addr: OperandRef<'tcx, &'ll Value>,
target_ty: Ty<'tcx>,
) -> &'ll Value {
// Determine the va_arg implementation to use. The LLVM va_arg instruction
// is lacking in some instances, so we should only use it as a fallback.
let target = &bx.cx.tcx.sess.target;
let layout = bx.cx.layout_of(target_ty);
let target_ty_size = layout.layout.size().bytes();
let target = &bx.cx.tcx.sess.target;
match target.arch {
Arch::X86 => emit_ptr_va_arg(
bx,
@ -1069,23 +1073,79 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::LoongArch32 => emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 2 * 4 { PassMode::Indirect } else { PassMode::Direct },
SlotSize::Bytes4,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::LoongArch64 => emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
SlotSize::Bytes8,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::AmdGpu => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes4,
AllowHigherAlign::No,
ForceRightAdjust::No,
),
Arch::Nvptx64 => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes1,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::Wasm32 => emit_ptr_va_arg(
bx,
addr,
target_ty,
if layout.is_aggregate() || layout.is_zst() || layout.is_1zst() {
PassMode::Indirect
} else {
PassMode::Direct
},
SlotSize::Bytes4,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::Wasm64 => bug!("c-variadic functions are not fully implemented for wasm64"),
Arch::CSky => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes4,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
// Windows x86_64
Arch::X86_64 if target.is_like_windows => {
let target_ty_size = bx.cx.size_of(target_ty).bytes();
emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
PassMode::Indirect
} else {
PassMode::Direct
},
SlotSize::Bytes8,
AllowHigherAlign::No,
ForceRightAdjust::No,
)
}
Arch::X86_64 if target.is_like_windows => emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
PassMode::Indirect
} else {
PassMode::Direct
},
SlotSize::Bytes8,
AllowHigherAlign::No,
ForceRightAdjust::No,
),
// This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64.
Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty),
Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty),

View file

@ -839,6 +839,11 @@ impl<'a> Linker for GccLinker<'a> {
self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error });
}
self.link_arg(path);
} else if self.sess.target.is_like_wasm {
self.link_arg("--no-export-dynamic");
for (sym, _) in symbols {
self.link_arg("--export").link_arg(sym);
}
} else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris {
let res: io::Result<()> = try {
let mut f = File::create_buffered(&path)?;
@ -853,11 +858,6 @@ impl<'a> Linker for GccLinker<'a> {
self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error });
}
self.link_arg("--dynamic-list").link_arg(path);
} else if self.sess.target.is_like_wasm {
self.link_arg("--no-export-dynamic");
for (sym, _) in symbols {
self.link_arg("--export").link_arg(sym);
}
} else {
// Write an LD version script
let res: io::Result<()> = try {

View file

@ -10,6 +10,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_session = { path = "../rustc_session" }

View file

@ -75,4 +75,8 @@ monomorphize_recursion_limit =
monomorphize_start_not_found = using `fn main` requires the standard library
.help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`
monomorphize_static_initializer_cyclic = static initializer forms a cycle involving `{$head}`
.label = part of this cycle
.note = cyclic static initializers are not supported for target `{$target}`
monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined

View file

@ -267,7 +267,8 @@ pub(crate) struct UsageMap<'tcx> {
// Maps every mono item to the mono items used by it.
pub used_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
// Maps every mono item to the mono items that use it.
// Maps each mono item with users to the mono items that use it.
// Be careful: subsets `used_map`, so unused items are vacant.
user_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
}

View file

@ -117,3 +117,15 @@ pub(crate) struct AbiRequiredTargetFeature<'a> {
/// Whether this is a problem at a call site or at a declaration.
pub is_call: bool,
}
#[derive(Diagnostic)]
#[diag(monomorphize_static_initializer_cyclic)]
#[note]
pub(crate) struct StaticInitializerCyclic<'a> {
#[primary_span]
pub span: Span,
#[label]
pub labels: Vec<Span>,
pub head: &'a str,
pub target: &'a str,
}

View file

@ -0,0 +1,18 @@
//! Checks that need to operate on the entire mono item graph
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::TyCtxt;
use crate::collector::UsageMap;
use crate::graph_checks::statics::check_static_initializers_are_acyclic;
mod statics;
pub(super) fn target_specific_checks<'tcx, 'a, 'b>(
tcx: TyCtxt<'tcx>,
mono_items: &'a [MonoItem<'tcx>],
usage_map: &'b UsageMap<'tcx>,
) {
if tcx.sess.target.options.static_initializer_must_be_acyclic {
check_static_initializers_are_acyclic(tcx, mono_items, usage_map);
}
}

View file

@ -0,0 +1,115 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::graph::{DirectedGraph, Successors};
use rustc_data_structures::unord::UnordMap;
use rustc_hir::def_id::DefId;
use rustc_index::{Idx, IndexVec, newtype_index};
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::TyCtxt;
use crate::collector::UsageMap;
use crate::errors;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct StaticNodeIdx(usize);
impl Idx for StaticNodeIdx {
fn new(idx: usize) -> Self {
Self(idx)
}
fn index(self) -> usize {
self.0
}
}
impl From<usize> for StaticNodeIdx {
fn from(value: usize) -> Self {
StaticNodeIdx(value)
}
}
newtype_index! {
#[derive(Ord, PartialOrd)]
struct StaticSccIdx {}
}
// Adjacency-list graph for statics using `StaticNodeIdx` as node type.
// We cannot use `DefId` as the node type directly because each node must be
// represented by an index in the range `0..num_nodes`.
struct StaticRefGraph<'a, 'b, 'tcx> {
// maps from `StaticNodeIdx` to `DefId` and vice versa
statics: &'a FxIndexSet<DefId>,
// contains for each `MonoItem` the `MonoItem`s it uses
used_map: &'b UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
}
impl<'a, 'b, 'tcx> DirectedGraph for StaticRefGraph<'a, 'b, 'tcx> {
type Node = StaticNodeIdx;
fn num_nodes(&self) -> usize {
self.statics.len()
}
}
impl<'a, 'b, 'tcx> Successors for StaticRefGraph<'a, 'b, 'tcx> {
fn successors(&self, node_idx: StaticNodeIdx) -> impl Iterator<Item = StaticNodeIdx> {
let def_id = self.statics[node_idx.index()];
self.used_map[&MonoItem::Static(def_id)].iter().filter_map(|&mono_item| match mono_item {
MonoItem::Static(def_id) => self.statics.get_index_of(&def_id).map(|idx| idx.into()),
_ => None,
})
}
}
pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>(
tcx: TyCtxt<'tcx>,
mono_items: &'a [MonoItem<'tcx>],
usage_map: &'b UsageMap<'tcx>,
) {
// Collect statics
let statics: FxIndexSet<DefId> = mono_items
.iter()
.filter_map(|&mono_item| match mono_item {
MonoItem::Static(def_id) => Some(def_id),
_ => None,
})
.collect();
// If we don't have any statics the check is not necessary
if statics.is_empty() {
return;
}
// Create a subgraph from the mono item graph, which only contains statics
let graph = StaticRefGraph { statics: &statics, used_map: &usage_map.used_map };
// Calculate its SCCs
let sccs: Sccs<StaticNodeIdx, StaticSccIdx> = Sccs::new(&graph);
// Group statics by SCCs
let mut nodes_of_sccs: IndexVec<StaticSccIdx, Vec<StaticNodeIdx>> =
IndexVec::from_elem_n(Vec::new(), sccs.num_sccs());
for i in graph.iter_nodes() {
nodes_of_sccs[sccs.scc(i)].push(i);
}
let is_cyclic = |nodes_of_scc: &[StaticNodeIdx]| -> bool {
match nodes_of_scc.len() {
0 => false,
1 => graph.successors(nodes_of_scc[0]).any(|x| x == nodes_of_scc[0]),
2.. => true,
}
};
// Emit errors for all cycles
for nodes in nodes_of_sccs.iter_mut().filter(|nodes| is_cyclic(nodes)) {
// We sort the nodes by their Span to have consistent error line numbers
nodes.sort_by_key(|node| tcx.def_span(statics[node.index()]));
let head_def = statics[nodes[0].index()];
let head_span = tcx.def_span(head_def);
tcx.dcx().emit_err(errors::StaticInitializerCyclic {
span: head_span,
labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(),
head: &tcx.def_path_str(head_def),
target: &tcx.sess.target.llvm_target,
});
}
}

View file

@ -16,6 +16,7 @@ use rustc_span::ErrorGuaranteed;
mod collector;
mod errors;
mod graph_checks;
mod mono_checks;
mod partitioning;
mod util;

View file

@ -124,6 +124,7 @@ use tracing::debug;
use crate::collector::{self, MonoItemCollectionStrategy, UsageMap};
use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined};
use crate::graph_checks::target_specific_checks;
struct PartitioningCx<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
@ -1135,6 +1136,8 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio
};
let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy);
// Perform checks that need to operate on the entire mono item graph
target_specific_checks(tcx, &items, &usage_map);
// If there was an error during collection (e.g. from one of the constants we evaluated),
// then we stop here. This way codegen does not have to worry about failing constants.

View file

@ -163,6 +163,7 @@ impl Target {
forward!(relro_level);
forward!(archive_format);
forward!(allow_asm);
forward!(static_initializer_must_be_acyclic);
forward!(main_needs_argc_argv);
forward!(has_thread_local);
forward!(obj_is_bitcode);
@ -360,6 +361,7 @@ impl ToJson for Target {
target_option_val!(relro_level);
target_option_val!(archive_format);
target_option_val!(allow_asm);
target_option_val!(static_initializer_must_be_acyclic);
target_option_val!(main_needs_argc_argv);
target_option_val!(has_thread_local);
target_option_val!(obj_is_bitcode);
@ -581,6 +583,7 @@ struct TargetSpecJson {
relro_level: Option<RelroLevel>,
archive_format: Option<StaticCow<str>>,
allow_asm: Option<bool>,
static_initializer_must_be_acyclic: Option<bool>,
main_needs_argc_argv: Option<bool>,
has_thread_local: Option<bool>,
obj_is_bitcode: Option<bool>,

View file

@ -2394,6 +2394,9 @@ pub struct TargetOptions {
pub archive_format: StaticCow<str>,
/// Is asm!() allowed? Defaults to true.
pub allow_asm: bool,
/// Static initializers must be acyclic.
/// Defaults to false
pub static_initializer_must_be_acyclic: bool,
/// Whether the runtime startup code requires the `main` function be passed
/// `argc` and `argv` values.
pub main_needs_argc_argv: bool,
@ -2777,6 +2780,7 @@ impl Default for TargetOptions {
archive_format: "gnu".into(),
main_needs_argc_argv: true,
allow_asm: true,
static_initializer_must_be_acyclic: false,
has_thread_local: false,
obj_is_bitcode: false,
min_atomic_width: None,

View file

@ -59,6 +59,9 @@ pub(crate) fn target() -> Target {
// Support using `self-contained` linkers like the llvm-bitcode-linker
link_self_contained: LinkSelfContainedDefault::True,
// Static initializers must not have cycles on this target
static_initializer_must_be_acyclic: true,
..Default::default()
},
}

View file

@ -127,6 +127,44 @@ impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for RawWaker {
}
}
/// Converts a closure into a [`Waker`].
///
/// The closure gets called every time the waker is woken.
///
/// # Examples
///
/// ```
/// #![feature(waker_fn)]
/// use std::task::waker_fn;
///
/// let waker = waker_fn(|| println!("woken"));
///
/// waker.wake_by_ref(); // Prints "woken".
/// waker.wake(); // Prints "woken".
/// ```
#[cfg(target_has_atomic = "ptr")]
#[unstable(feature = "waker_fn", issue = "149580")]
pub fn waker_fn<F: Fn() + Send + Sync + 'static>(f: F) -> Waker {
struct WakeFn<F> {
f: F,
}
impl<F> Wake for WakeFn<F>
where
F: Fn(),
{
fn wake(self: Arc<Self>) {
(self.f)()
}
fn wake_by_ref(self: &Arc<Self>) {
(self.f)()
}
}
Waker::from(Arc::new(WakeFn { f }))
}
// NB: This private function for constructing a RawWaker is used, rather than
// inlining this into the `From<Arc<W>> for RawWaker` impl, to ensure that
// the safety of `From<Arc<W>> for Waker` does not depend on the correct
@ -306,6 +344,45 @@ impl<W: LocalWake + 'static> From<Rc<W>> for RawWaker {
}
}
/// Converts a closure into a [`LocalWaker`].
///
/// The closure gets called every time the local waker is woken.
///
/// # Examples
///
/// ```
/// #![feature(local_waker)]
/// #![feature(waker_fn)]
/// use std::task::local_waker_fn;
///
/// let waker = local_waker_fn(|| println!("woken"));
///
/// waker.wake_by_ref(); // Prints "woken".
/// waker.wake(); // Prints "woken".
/// ```
// #[unstable(feature = "local_waker", issue = "118959")]
#[unstable(feature = "waker_fn", issue = "149580")]
pub fn local_waker_fn<F: Fn() + Send + Sync + 'static>(f: F) -> LocalWaker {
struct LocalWakeFn<F> {
f: F,
}
impl<F> LocalWake for LocalWakeFn<F>
where
F: Fn(),
{
fn wake(self: Rc<Self>) {
(self.f)()
}
fn wake_by_ref(self: &Rc<Self>) {
(self.f)()
}
}
LocalWaker::from(Rc::new(LocalWakeFn { f }))
}
// NB: This private function for constructing a RawWaker is used, rather than
// inlining this into the `From<Rc<W>> for RawWaker` impl, to ensure that
// the safety of `From<Rc<W>> for Waker` does not depend on the correct

View file

@ -205,6 +205,56 @@ pub trait Error: Debug + Display {
/// assert!(request_ref::<MyLittleTeaPot>(dyn_error).is_none());
/// }
/// ```
///
/// # Delegating Impls
///
/// <div class="warning">
///
/// **Warning**: We recommend implementors avoid delegating implementations of `provide` to
/// source error implementations.
///
/// </div>
///
/// This method should expose context from the current piece of the source chain only, not from
/// sources that are exposed in the chain of sources. Delegating `provide` implementations cause
/// the same context to be provided by multiple errors in the chain of sources which can cause
/// unintended duplication of information in error reports or require heuristics to deduplicate.
///
/// In other words, the following implementation pattern for `provide` is discouraged and should
/// not be used for [`Error`] types exposed in public APIs to third parties.
///
/// ```rust
/// # #![feature(error_generic_member_access)]
/// # use core::fmt;
/// # use core::error::Request;
/// # #[derive(Debug)]
/// struct MyError {
/// source: Error,
/// }
/// # #[derive(Debug)]
/// # struct Error;
/// # impl fmt::Display for Error {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "Example Source Error")
/// # }
/// # }
/// # impl fmt::Display for MyError {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "Example Error")
/// # }
/// # }
/// # impl std::error::Error for Error { }
///
/// impl std::error::Error for MyError {
/// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
/// Some(&self.source)
/// }
///
/// fn provide<'a>(&'a self, request: &mut Request<'a>) {
/// self.source.provide(request) // <--- Discouraged
/// }
/// }
/// ```
#[unstable(feature = "error_generic_member_access", issue = "99301")]
#[allow(unused_variables)]
fn provide<'a>(&'a self, request: &mut Request<'a>) {}

View file

@ -2738,10 +2738,6 @@ pub unsafe fn vtable_align(ptr: *const ()) -> usize;
/// Determining whether `T` can be coerced to the trait object type `U` requires trait resolution by the compiler.
/// In some cases, that resolution can exceed the recursion limit,
/// and compilation will fail instead of this function returning `None`.
///
/// # Safety
///
/// `ptr` must point to a vtable.
#[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_intrinsic]

View file

@ -1550,6 +1550,24 @@ impl<T> AtomicPtr<T> {
unsafe { &*ptr.cast() }
}
/// Creates a new `AtomicPtr` initialized with a null pointer.
///
/// # Examples
///
/// ```
/// #![feature(atomic_ptr_null)]
/// use std::sync::atomic::{AtomicPtr, Ordering};
///
/// let atomic_ptr = AtomicPtr::<()>::null();
/// assert!(atomic_ptr.load(Ordering::Relaxed).is_null());
/// ```
#[inline]
#[must_use]
#[unstable(feature = "atomic_ptr_null", issue = "150733")]
pub const fn null() -> AtomicPtr<T> {
AtomicPtr::new(crate::ptr::null_mut())
}
/// Returns a mutable reference to the underlying pointer.
///
/// This is safe because the mutable reference guarantees that no other threads are

View file

@ -793,8 +793,7 @@ tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustcPrivate
tool_check_step!(Miri {
path: "src/tools/miri",
mode: Mode::ToolRustcPrivate,
enable_features: ["stack-cache"],
default_features: false,
enable_features: ["check_only"],
});
tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustcPrivate });
tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustcPrivate });

View file

@ -2265,6 +2265,8 @@ Please disable assertions with `rust.debug-assertions = false`.
cmd.arg("--with-std-remap-debuginfo");
}
cmd.arg("--jobs").arg(builder.jobs().to_string());
let mut llvm_components_passed = false;
let mut copts_passed = false;
if builder.config.llvm_enabled(test_compiler.host) {

View file

@ -49,6 +49,39 @@ $ rustup component add llvm-tools --toolchain nightly
$ rustup component add llvm-bitcode-linker --toolchain nightly
```
## Target specific restrictions
The PTX instruction set architecture has special requirements regarding what is
and isn't allowed. In order to avoid producing invalid PTX or generating undefined
behavior by LLVM, some Rust language features are disallowed when compiling for this target.
### Static initializers must be acyclic
A static's initializer must not form a cycle with itself or another static's
initializer. Therefore, the compiler will reject not only the self-referencing static `A`,
but all of the following statics.
```Rust
struct Foo(&'static Foo);
static A: Foo = Foo(&A); //~ ERROR static initializer forms a cycle involving `A`
static B0: Foo = Foo(&B1); //~ ERROR static initializer forms a cycle involving `B0`
static B1: Foo = Foo(&B0);
static C0: Foo = Foo(&C1); //~ ERROR static initializer forms a cycle involving `C0`
static C1: Foo = Foo(&C2);
static C2: Foo = Foo(&C0);
```
Initializers that are acyclic are allowed:
```Rust
struct Bar(&'static u32);
static BAR: Bar = Bar(&INT); // is allowed
static INT: u32 = 42u32; // also allowed
```
<!-- FIXME: fill this out

View file

@ -7,6 +7,7 @@ IBM z/Architecture (s390x) targets (including IBM Z and LinuxONE) running Linux.
## Target maintainers
[@uweigand](https://github.com/uweigand)
[@Gelbpunkt](https://github.com/Gelbpunkt)
## Requirements

View file

@ -715,6 +715,11 @@ pub struct Config {
pub override_codegen_backend: Option<String>,
/// Whether to ignore `//@ ignore-backends`.
pub bypass_ignore_backends: bool,
/// Number of parallel jobs configured for the build.
///
/// This is forwarded from bootstrap's `jobs` configuration.
pub jobs: u32,
}
impl Config {

View file

@ -225,6 +225,7 @@ impl ConfigBuilder {
"--nightly-branch=",
"--git-merge-commit-email=",
"--minicore-path=",
"--jobs=0",
];
let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();

View file

@ -218,7 +218,8 @@ fn parse_config(args: Vec<String>) -> Config {
"the codegen backend to use instead of the default one",
"CODEGEN BACKEND [NAME | PATH]",
)
.optflag("", "bypass-ignore-backends", "ignore `//@ ignore-backends` directives");
.optflag("", "bypass-ignore-backends", "ignore `//@ ignore-backends` directives")
.reqopt("", "jobs", "number of parallel jobs bootstrap was configured with", "JOBS");
let (argv0, args_) = args.split_first().unwrap();
if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
@ -363,6 +364,11 @@ fn parse_config(args: Vec<String>) -> Config {
let build_test_suite_root = opt_path(matches, "build-test-suite-root");
assert!(build_test_suite_root.starts_with(&build_root));
let jobs = match matches.opt_str("jobs") {
Some(jobs) => jobs.parse::<u32>().expect("expected `--jobs` to be an `u32`"),
None => panic!("`--jobs` is required"),
};
Config {
bless: matches.opt_present("bless"),
fail_fast: matches.opt_present("fail-fast")
@ -481,6 +487,8 @@ fn parse_config(args: Vec<String>) -> Config {
default_codegen_backend,
override_codegen_backend,
bypass_ignore_backends: matches.opt_present("bypass-ignore-backends"),
jobs,
}
}

View file

@ -249,6 +249,9 @@ impl TestCx<'_> {
cmd.env("__STD_REMAP_DEBUGINFO_ENABLED", "1");
}
// Used for `run_make_support::env::jobs`.
cmd.env("__BOOTSTRAP_JOBS", self.config.jobs.to_string());
// We don't want RUSTFLAGS set from the outside to interfere with
// compiler flags set in the test cases:
cmd.env_remove("RUSTFLAGS");

View file

@ -139,5 +139,6 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config {
default_codegen_backend: CodegenBackend::Llvm,
override_codegen_backend: None,
bypass_ignore_backends: Default::default(),
jobs: Default::default(),
}
}

View file

@ -49,3 +49,17 @@ pub fn set_current_dir<P: AsRef<std::path::Path>>(dir: P) {
std::env::set_current_dir(dir.as_ref())
.expect(&format!("could not set current directory to \"{}\"", dir.as_ref().display()));
}
/// Number of parallel jobs bootstrap was configured with.
///
/// This may fallback to [`std::thread::available_parallelism`] when no explicit jobs count has been
/// configured. Refer to bootstrap's jobs fallback logic.
#[track_caller]
pub fn jobs() -> u32 {
std::env::var_os("__BOOTSTRAP_JOBS")
.expect("`__BOOTSTRAP_JOBS` must be set by `compiletest`")
.to_str()
.expect("`__BOOTSTRAP_JOBS` must be a valid string")
.parse::<u32>()
.expect("`__BOOTSTRAP_JOBS` must be a valid `u32`")
}

View file

@ -211,7 +211,7 @@ impl Neg for isize {
}
#[lang = "sync"]
trait Sync {}
pub trait Sync {}
impl_marker_trait!(
Sync => [
char, bool,

View file

@ -0,0 +1,5 @@
//! Very basic smoke test to make sure `run_make_support::env::jobs` at least does not panic.
fn main() {
println!("{}", run_make_support::env::jobs());
}

View file

@ -0,0 +1,29 @@
//@ add-minicore
//@ needs-llvm-components: nvptx
//@ compile-flags: --target nvptx64-nvidia-cuda --emit link
//@ ignore-backends: gcc
#![crate_type = "rlib"]
#![feature(no_core)]
#![no_std]
#![no_core]
extern crate minicore;
use minicore::*;
struct Foo(&'static Foo);
impl Sync for Foo {}
static A: Foo = Foo(&A); //~ ERROR static initializer forms a cycle involving `A`
static B0: Foo = Foo(&B1); //~ ERROR static initializer forms a cycle involving `B0`
static B1: Foo = Foo(&B0);
static C0: Foo = Foo(&C1); //~ ERROR static initializer forms a cycle involving `C0`
static C1: Foo = Foo(&C2);
static C2: Foo = Foo(&C0);
struct Bar(&'static u32);
impl Sync for Bar {}
static BAR: Bar = Bar(&INT);
static INT: u32 = 42u32;

View file

@ -0,0 +1,32 @@
error: static initializer forms a cycle involving `C0`
--> $DIR/static-initializer-acyclic-issue-146787.rs:21:1
|
LL | static C0: Foo = Foo(&C1);
| ^^^^^^^^^^^^^^ part of this cycle
LL | static C1: Foo = Foo(&C2);
| -------------- part of this cycle
LL | static C2: Foo = Foo(&C0);
| -------------- part of this cycle
|
= note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda`
error: static initializer forms a cycle involving `B0`
--> $DIR/static-initializer-acyclic-issue-146787.rs:18:1
|
LL | static B0: Foo = Foo(&B1);
| ^^^^^^^^^^^^^^ part of this cycle
LL | static B1: Foo = Foo(&B0);
| -------------- part of this cycle
|
= note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda`
error: static initializer forms a cycle involving `A`
--> $DIR/static-initializer-acyclic-issue-146787.rs:16:1
|
LL | static A: Foo = Foo(&A);
| ^^^^^^^^^^^^^ part of this cycle
|
= note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda`
error: aborting due to 3 previous errors