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:
commit
4586feb998
32 changed files with 537 additions and 37 deletions
|
|
@ -4421,6 +4421,7 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_session",
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>>>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
18
compiler/rustc_monomorphize/src/graph_checks/mod.rs
Normal file
18
compiler/rustc_monomorphize/src/graph_checks/mod.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
115
compiler/rustc_monomorphize/src/graph_checks/statics.rs
Normal file
115
compiler/rustc_monomorphize/src/graph_checks/statics.rs
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ use rustc_span::ErrorGuaranteed;
|
|||
|
||||
mod collector;
|
||||
mod errors;
|
||||
mod graph_checks;
|
||||
mod mono_checks;
|
||||
mod partitioning;
|
||||
mod util;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>) {}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ impl Neg for isize {
|
|||
}
|
||||
|
||||
#[lang = "sync"]
|
||||
trait Sync {}
|
||||
pub trait Sync {}
|
||||
impl_marker_trait!(
|
||||
Sync => [
|
||||
char, bool,
|
||||
|
|
|
|||
5
tests/run-make/compiletest-self-test/jobs/rmake.rs
Normal file
5
tests/run-make/compiletest-self-test/jobs/rmake.rs
Normal 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());
|
||||
}
|
||||
29
tests/ui/static/static-initializer-acyclic-issue-146787.rs
Normal file
29
tests/ui/static/static-initializer-acyclic-issue-146787.rs
Normal 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;
|
||||
|
|
@ -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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue