mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #115767 - matthiaskrgr:rollup-byf3lvq, r=matthiaskrgr
Rollup of 8 pull requests Successful merges: - #115548 (Extract parallel operations in `rustc_data_structures::sync` into a new `parallel` submodule) - #115591 (Add regression test for LLVM 17-rc3 miscompile) - #115631 (Don't ICE when computing ctype's `repr_nullable_ptr` for possibly-unsized ty) - #115708 (fix homogeneous_aggregate not ignoring some ZST) - #115730 (Some more small driver refactors) - #115749 (Allow loading the SMIR for constants and statics) - #115757 (Add a test for #108030) - #115761 (Update books) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
b4e54c6e39
@ -41,12 +41,9 @@
|
||||
//! [^2] `MTLockRef` is a typedef.
|
||||
|
||||
pub use crate::marker::*;
|
||||
use parking_lot::Mutex;
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
|
||||
mod lock;
|
||||
pub use lock::{Lock, LockGuard, Mode};
|
||||
@ -54,6 +51,11 @@ pub use lock::{Lock, LockGuard, Mode};
|
||||
mod worker_local;
|
||||
pub use worker_local::{Registry, WorkerLocal};
|
||||
|
||||
mod parallel;
|
||||
#[cfg(parallel_compiler)]
|
||||
pub use parallel::scope;
|
||||
pub use parallel::{join, par_for_each_in, par_map, parallel_guard};
|
||||
|
||||
pub use std::sync::atomic::Ordering;
|
||||
pub use std::sync::atomic::Ordering::SeqCst;
|
||||
|
||||
@ -107,37 +109,6 @@ mod mode {
|
||||
|
||||
pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode};
|
||||
|
||||
/// A guard used to hold panics that occur during a parallel section to later by unwound.
|
||||
/// This is used for the parallel compiler to prevent fatal errors from non-deterministically
|
||||
/// hiding errors by ensuring that everything in the section has completed executing before
|
||||
/// continuing with unwinding. It's also used for the non-parallel code to ensure error message
|
||||
/// output match the parallel compiler for testing purposes.
|
||||
pub struct ParallelGuard {
|
||||
panic: Mutex<Option<Box<dyn Any + std::marker::Send + 'static>>>,
|
||||
}
|
||||
|
||||
impl ParallelGuard {
|
||||
pub fn run<R>(&self, f: impl FnOnce() -> R) -> Option<R> {
|
||||
catch_unwind(AssertUnwindSafe(f))
|
||||
.map_err(|err| {
|
||||
*self.panic.lock() = Some(err);
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// This gives access to a fresh parallel guard in the closure and will unwind any panics
|
||||
/// caught in it after the closure returns.
|
||||
#[inline]
|
||||
pub fn parallel_guard<R>(f: impl FnOnce(&ParallelGuard) -> R) -> R {
|
||||
let guard = ParallelGuard { panic: Mutex::new(None) };
|
||||
let ret = f(&guard);
|
||||
if let Some(panic) = guard.panic.into_inner() {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(not(parallel_compiler))] {
|
||||
use std::ops::Add;
|
||||
@ -229,44 +200,6 @@ cfg_if! {
|
||||
pub type AtomicU32 = Atomic<u32>;
|
||||
pub type AtomicU64 = Atomic<u64>;
|
||||
|
||||
pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
|
||||
where A: FnOnce() -> RA,
|
||||
B: FnOnce() -> RB
|
||||
{
|
||||
let (a, b) = parallel_guard(|guard| {
|
||||
let a = guard.run(oper_a);
|
||||
let b = guard.run(oper_b);
|
||||
(a, b)
|
||||
});
|
||||
(a.unwrap(), b.unwrap())
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! parallel {
|
||||
($($blocks:block),*) => {{
|
||||
$crate::sync::parallel_guard(|guard| {
|
||||
$(guard.run(|| $blocks);)*
|
||||
});
|
||||
}}
|
||||
}
|
||||
|
||||
pub fn par_for_each_in<T: IntoIterator>(t: T, mut for_each: impl FnMut(T::Item) + Sync + Send) {
|
||||
parallel_guard(|guard| {
|
||||
t.into_iter().for_each(|i| {
|
||||
guard.run(|| for_each(i));
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pub fn par_map<T: IntoIterator, R, C: FromIterator<R>>(
|
||||
t: T,
|
||||
mut map: impl FnMut(<<T as IntoIterator>::IntoIter as Iterator>::Item) -> R,
|
||||
) -> C {
|
||||
parallel_guard(|guard| {
|
||||
t.into_iter().filter_map(|i| guard.run(|| map(i))).collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub use std::rc::Rc as Lrc;
|
||||
pub use std::rc::Weak as Weak;
|
||||
pub use std::cell::Ref as ReadGuard;
|
||||
@ -372,105 +305,6 @@ cfg_if! {
|
||||
|
||||
use std::thread;
|
||||
|
||||
#[inline]
|
||||
pub fn join<A, B, RA: DynSend, RB: DynSend>(oper_a: A, oper_b: B) -> (RA, RB)
|
||||
where
|
||||
A: FnOnce() -> RA + DynSend,
|
||||
B: FnOnce() -> RB + DynSend,
|
||||
{
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let oper_a = FromDyn::from(oper_a);
|
||||
let oper_b = FromDyn::from(oper_b);
|
||||
let (a, b) = rayon::join(move || FromDyn::from(oper_a.into_inner()()), move || FromDyn::from(oper_b.into_inner()()));
|
||||
(a.into_inner(), b.into_inner())
|
||||
} else {
|
||||
let (a, b) = parallel_guard(|guard| {
|
||||
let a = guard.run(oper_a);
|
||||
let b = guard.run(oper_b);
|
||||
(a, b)
|
||||
});
|
||||
(a.unwrap(), b.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
// This function only works when `mode::is_dyn_thread_safe()`.
|
||||
pub fn scope<'scope, OP, R>(op: OP) -> R
|
||||
where
|
||||
OP: FnOnce(&rayon::Scope<'scope>) -> R + DynSend,
|
||||
R: DynSend,
|
||||
{
|
||||
let op = FromDyn::from(op);
|
||||
rayon::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner()
|
||||
}
|
||||
|
||||
/// Runs a list of blocks in parallel. The first block is executed immediately on
|
||||
/// the current thread. Use that for the longest running block.
|
||||
#[macro_export]
|
||||
macro_rules! parallel {
|
||||
(impl $fblock:block [$($c:expr,)*] [$block:expr $(, $rest:expr)*]) => {
|
||||
parallel!(impl $fblock [$block, $($c,)*] [$($rest),*])
|
||||
};
|
||||
(impl $fblock:block [$($blocks:expr,)*] []) => {
|
||||
::rustc_data_structures::sync::scope(|s| {
|
||||
$(let block = rustc_data_structures::sync::FromDyn::from(|| $blocks);
|
||||
s.spawn(move |_| block.into_inner()());)*
|
||||
(|| $fblock)();
|
||||
});
|
||||
};
|
||||
($fblock:block, $($blocks:block),*) => {
|
||||
if rustc_data_structures::sync::is_dyn_thread_safe() {
|
||||
// Reverse the order of the later blocks since Rayon executes them in reverse order
|
||||
// when using a single thread. This ensures the execution order matches that
|
||||
// of a single threaded rustc.
|
||||
parallel!(impl $fblock [] [$($blocks),*]);
|
||||
} else {
|
||||
$crate::sync::parallel_guard(|guard| {
|
||||
guard.run(|| $fblock);
|
||||
$(guard.run(|| $blocks);)*
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator};
|
||||
|
||||
pub fn par_for_each_in<I, T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>>(
|
||||
t: T,
|
||||
for_each: impl Fn(I) + DynSync + DynSend
|
||||
) {
|
||||
parallel_guard(|guard| {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let for_each = FromDyn::from(for_each);
|
||||
t.into_par_iter().for_each(|i| {
|
||||
guard.run(|| for_each(i));
|
||||
});
|
||||
} else {
|
||||
t.into_iter().for_each(|i| {
|
||||
guard.run(|| for_each(i));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn par_map<
|
||||
I,
|
||||
T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>,
|
||||
R: std::marker::Send,
|
||||
C: FromIterator<R> + FromParallelIterator<R>
|
||||
>(
|
||||
t: T,
|
||||
map: impl Fn(I) -> R + DynSync + DynSend
|
||||
) -> C {
|
||||
parallel_guard(|guard| {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let map = FromDyn::from(map);
|
||||
t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect()
|
||||
} else {
|
||||
t.into_iter().filter_map(|i| guard.run(|| map(i))).collect()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// This makes locks panic if they are already held.
|
||||
/// It is only useful when you are running in a single thread
|
||||
const ERROR_CHECKING: bool = false;
|
||||
|
188
compiler/rustc_data_structures/src/sync/parallel.rs
Normal file
188
compiler/rustc_data_structures/src/sync/parallel.rs
Normal file
@ -0,0 +1,188 @@
|
||||
//! This module defines parallel operations that are implemented in
|
||||
//! one way for the serial compiler, and another way the parallel compiler.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use std::any::Any;
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
|
||||
#[cfg(not(parallel_compiler))]
|
||||
pub use disabled::*;
|
||||
#[cfg(parallel_compiler)]
|
||||
pub use enabled::*;
|
||||
|
||||
/// A guard used to hold panics that occur during a parallel section to later by unwound.
|
||||
/// This is used for the parallel compiler to prevent fatal errors from non-deterministically
|
||||
/// hiding errors by ensuring that everything in the section has completed executing before
|
||||
/// continuing with unwinding. It's also used for the non-parallel code to ensure error message
|
||||
/// output match the parallel compiler for testing purposes.
|
||||
pub struct ParallelGuard {
|
||||
panic: Mutex<Option<Box<dyn Any + Send + 'static>>>,
|
||||
}
|
||||
|
||||
impl ParallelGuard {
|
||||
pub fn run<R>(&self, f: impl FnOnce() -> R) -> Option<R> {
|
||||
catch_unwind(AssertUnwindSafe(f))
|
||||
.map_err(|err| {
|
||||
*self.panic.lock() = Some(err);
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// This gives access to a fresh parallel guard in the closure and will unwind any panics
|
||||
/// caught in it after the closure returns.
|
||||
#[inline]
|
||||
pub fn parallel_guard<R>(f: impl FnOnce(&ParallelGuard) -> R) -> R {
|
||||
let guard = ParallelGuard { panic: Mutex::new(None) };
|
||||
let ret = f(&guard);
|
||||
if let Some(panic) = guard.panic.into_inner() {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
mod disabled {
|
||||
use crate::sync::parallel_guard;
|
||||
|
||||
#[macro_export]
|
||||
#[cfg(not(parallel_compiler))]
|
||||
macro_rules! parallel {
|
||||
($($blocks:block),*) => {{
|
||||
$crate::sync::parallel_guard(|guard| {
|
||||
$(guard.run(|| $blocks);)*
|
||||
});
|
||||
}}
|
||||
}
|
||||
|
||||
pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
|
||||
where
|
||||
A: FnOnce() -> RA,
|
||||
B: FnOnce() -> RB,
|
||||
{
|
||||
let (a, b) = parallel_guard(|guard| {
|
||||
let a = guard.run(oper_a);
|
||||
let b = guard.run(oper_b);
|
||||
(a, b)
|
||||
});
|
||||
(a.unwrap(), b.unwrap())
|
||||
}
|
||||
|
||||
pub fn par_for_each_in<T: IntoIterator>(t: T, mut for_each: impl FnMut(T::Item)) {
|
||||
parallel_guard(|guard| {
|
||||
t.into_iter().for_each(|i| {
|
||||
guard.run(|| for_each(i));
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pub fn par_map<T: IntoIterator, R, C: FromIterator<R>>(
|
||||
t: T,
|
||||
mut map: impl FnMut(<<T as IntoIterator>::IntoIter as Iterator>::Item) -> R,
|
||||
) -> C {
|
||||
parallel_guard(|guard| t.into_iter().filter_map(|i| guard.run(|| map(i))).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
mod enabled {
|
||||
use crate::sync::{mode, parallel_guard, DynSend, DynSync, FromDyn};
|
||||
|
||||
/// Runs a list of blocks in parallel. The first block is executed immediately on
|
||||
/// the current thread. Use that for the longest running block.
|
||||
#[macro_export]
|
||||
macro_rules! parallel {
|
||||
(impl $fblock:block [$($c:expr,)*] [$block:expr $(, $rest:expr)*]) => {
|
||||
parallel!(impl $fblock [$block, $($c,)*] [$($rest),*])
|
||||
};
|
||||
(impl $fblock:block [$($blocks:expr,)*] []) => {
|
||||
::rustc_data_structures::sync::scope(|s| {
|
||||
$(let block = rustc_data_structures::sync::FromDyn::from(|| $blocks);
|
||||
s.spawn(move |_| block.into_inner()());)*
|
||||
(|| $fblock)();
|
||||
});
|
||||
};
|
||||
($fblock:block, $($blocks:block),*) => {
|
||||
if rustc_data_structures::sync::is_dyn_thread_safe() {
|
||||
// Reverse the order of the later blocks since Rayon executes them in reverse order
|
||||
// when using a single thread. This ensures the execution order matches that
|
||||
// of a single threaded rustc.
|
||||
parallel!(impl $fblock [] [$($blocks),*]);
|
||||
} else {
|
||||
$crate::sync::parallel_guard(|guard| {
|
||||
guard.run(|| $fblock);
|
||||
$(guard.run(|| $blocks);)*
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// This function only works when `mode::is_dyn_thread_safe()`.
|
||||
pub fn scope<'scope, OP, R>(op: OP) -> R
|
||||
where
|
||||
OP: FnOnce(&rayon::Scope<'scope>) -> R + DynSend,
|
||||
R: DynSend,
|
||||
{
|
||||
let op = FromDyn::from(op);
|
||||
rayon::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn join<A, B, RA: DynSend, RB: DynSend>(oper_a: A, oper_b: B) -> (RA, RB)
|
||||
where
|
||||
A: FnOnce() -> RA + DynSend,
|
||||
B: FnOnce() -> RB + DynSend,
|
||||
{
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let oper_a = FromDyn::from(oper_a);
|
||||
let oper_b = FromDyn::from(oper_b);
|
||||
let (a, b) = rayon::join(
|
||||
move || FromDyn::from(oper_a.into_inner()()),
|
||||
move || FromDyn::from(oper_b.into_inner()()),
|
||||
);
|
||||
(a.into_inner(), b.into_inner())
|
||||
} else {
|
||||
super::disabled::join(oper_a, oper_b)
|
||||
}
|
||||
}
|
||||
|
||||
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator};
|
||||
|
||||
pub fn par_for_each_in<I, T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>>(
|
||||
t: T,
|
||||
for_each: impl Fn(I) + DynSync + DynSend,
|
||||
) {
|
||||
parallel_guard(|guard| {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let for_each = FromDyn::from(for_each);
|
||||
t.into_par_iter().for_each(|i| {
|
||||
guard.run(|| for_each(i));
|
||||
});
|
||||
} else {
|
||||
t.into_iter().for_each(|i| {
|
||||
guard.run(|| for_each(i));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn par_map<
|
||||
I,
|
||||
T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>,
|
||||
R: std::marker::Send,
|
||||
C: FromIterator<R> + FromParallelIterator<R>,
|
||||
>(
|
||||
t: T,
|
||||
map: impl Fn(I) -> R + DynSync + DynSend,
|
||||
) -> C {
|
||||
parallel_guard(|guard| {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let map = FromDyn::from(map);
|
||||
t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect()
|
||||
} else {
|
||||
t.into_iter().filter_map(|i| guard.run(|| map(i))).collect()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -162,9 +162,10 @@ pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T
|
||||
pub trait Callbacks {
|
||||
/// Called before creating the compiler instance
|
||||
fn config(&mut self, _config: &mut interface::Config) {}
|
||||
/// Called after parsing. Return value instructs the compiler whether to
|
||||
/// Called after parsing the crate root. Submodules are not yet parsed when
|
||||
/// this callback is called. Return value instructs the compiler whether to
|
||||
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
|
||||
fn after_parsing<'tcx>(
|
||||
fn after_crate_root_parsing<'tcx>(
|
||||
&mut self,
|
||||
_compiler: &interface::Compiler,
|
||||
_queries: &'tcx Queries<'tcx>,
|
||||
@ -184,7 +185,6 @@ pub trait Callbacks {
|
||||
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
|
||||
fn after_analysis<'tcx>(
|
||||
&mut self,
|
||||
_handler: &EarlyErrorHandler,
|
||||
_compiler: &interface::Compiler,
|
||||
_queries: &'tcx Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
@ -407,7 +407,7 @@ fn run_compiler(
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
|
||||
if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop {
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
@ -445,7 +445,7 @@ fn run_compiler(
|
||||
|
||||
queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?;
|
||||
|
||||
if callbacks.after_analysis(&handler, compiler, queries) == Compilation::Stop {
|
||||
if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,7 @@ impl<'tcx> Queries<'tcx> {
|
||||
.compute(|| passes::parse(self.session()).map_err(|mut parse_error| parse_error.emit()))
|
||||
}
|
||||
|
||||
#[deprecated = "pre_configure may be made private in the future. If you need it please open an issue with your use case."]
|
||||
pub fn pre_configure(&self) -> Result<QueryResult<'_, (ast::Crate, ast::AttrVec)>> {
|
||||
self.pre_configure.compute(|| {
|
||||
let mut krate = self.parse()?.steal();
|
||||
@ -171,6 +172,7 @@ impl<'tcx> Queries<'tcx> {
|
||||
pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, &'tcx GlobalCtxt<'tcx>>> {
|
||||
self.gcx.compute(|| {
|
||||
let sess = self.session();
|
||||
#[allow(deprecated)]
|
||||
let (krate, pre_configured_attrs) = self.pre_configure()?.steal();
|
||||
|
||||
// parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
|
||||
|
@ -923,7 +923,12 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
|
||||
}
|
||||
|
||||
// Return the nullable type this Option-like enum can be safely represented with.
|
||||
let field_ty_abi = &tcx.layout_of(param_env.and(field_ty)).unwrap().abi;
|
||||
let field_ty_layout = tcx.layout_of(param_env.and(field_ty));
|
||||
if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
|
||||
bug!("should be able to compute the layout of non-polymorphic type");
|
||||
}
|
||||
|
||||
let field_ty_abi = &field_ty_layout.ok()?.abi;
|
||||
if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
|
||||
match field_ty_scalar.valid_range(&tcx) {
|
||||
WrappingRange { start: 0, end }
|
||||
|
@ -16,7 +16,6 @@ use rustc_driver::{Callbacks, Compilation, RunCompiler};
|
||||
use rustc_interface::{interface, Queries};
|
||||
use rustc_middle::mir::interpret::AllocId;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::EarlyErrorHandler;
|
||||
pub use rustc_span::def_id::{CrateNum, DefId};
|
||||
|
||||
fn with_tables<R>(mut f: impl FnMut(&mut Tables<'_>) -> R) -> R {
|
||||
@ -233,7 +232,6 @@ where
|
||||
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
|
||||
fn after_analysis<'tcx>(
|
||||
&mut self,
|
||||
_handler: &EarlyErrorHandler,
|
||||
_compiler: &interface::Compiler,
|
||||
queries: &'tcx Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
|
@ -84,7 +84,7 @@ impl<'tcx> Context for Tables<'tcx> {
|
||||
|
||||
fn mir_body(&mut self, item: stable_mir::DefId) -> stable_mir::mir::Body {
|
||||
let def_id = self[item];
|
||||
let mir = self.tcx.optimized_mir(def_id);
|
||||
let mir = self.tcx.instance_mir(ty::InstanceDef::Item(def_id));
|
||||
stable_mir::mir::Body {
|
||||
blocks: mir
|
||||
.basic_blocks
|
||||
|
@ -382,8 +382,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
||||
/// only a single type (e.g., `(u32, u32)`). Such aggregates are often
|
||||
/// special-cased in ABIs.
|
||||
///
|
||||
/// Note: We generally ignore fields of zero-sized type when computing
|
||||
/// this value (see #56877).
|
||||
/// Note: We generally ignore 1-ZST fields when computing this value (see #56877).
|
||||
///
|
||||
/// This is public so that it can be used in unit tests, but
|
||||
/// should generally only be relevant to the ABI details of
|
||||
@ -441,11 +440,17 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
||||
let mut total = start;
|
||||
|
||||
for i in 0..layout.fields.count() {
|
||||
if !is_union && total != layout.fields.offset(i) {
|
||||
return Err(Heterogeneous);
|
||||
let field = layout.field(cx, i);
|
||||
if field.is_1zst() {
|
||||
// No data here and no impact on layout, can be ignored.
|
||||
// (We might be able to also ignore all aligned ZST but that's less clear.)
|
||||
continue;
|
||||
}
|
||||
|
||||
let field = layout.field(cx, i);
|
||||
if !is_union && total != layout.fields.offset(i) {
|
||||
// This field isn't just after the previous one we considered, abort.
|
||||
return Err(Heterogeneous);
|
||||
}
|
||||
|
||||
result = result.merge(field.homogeneous_aggregate(cx)?)?;
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 2751bdcef125468ea2ee006c11992cd1405aebe5
|
||||
Subproject commit 34fca48ed284525b2f124bf93c51af36d6685492
|
@ -1 +1 @@
|
||||
Subproject commit 388750b081c0893c275044d37203f97709e058ba
|
||||
Subproject commit e3f3af69dce71cd37a785bccb7e58449197d940c
|
@ -1 +1 @@
|
||||
Subproject commit d43038932adeb16ada80e206d4c073d851298101
|
||||
Subproject commit ee7c676fd6e287459cb407337652412c990686c0
|
@ -1 +1 @@
|
||||
Subproject commit 07e0df2f006e59d171c6bf3cafa9d61dbeb520d8
|
||||
Subproject commit c954202c1e1720cba5628f99543cc01188c7d6fc
|
@ -1 +1 @@
|
||||
Subproject commit b123ab4754127d822ffb38349ce0fbf561f1b2fd
|
||||
Subproject commit 08bb147d51e815b96e8db7ba4cf870f201c11ff8
|
@ -59,7 +59,6 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
||||
|
||||
fn after_analysis<'tcx>(
|
||||
&mut self,
|
||||
handler: &EarlyErrorHandler,
|
||||
_: &rustc_interface::interface::Compiler,
|
||||
queries: &'tcx rustc_interface::Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
@ -68,7 +67,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
||||
tcx.sess.fatal("miri cannot be run on programs that fail compilation");
|
||||
}
|
||||
|
||||
init_late_loggers(handler, tcx);
|
||||
let handler = EarlyErrorHandler::new(tcx.sess.opts.error_format);
|
||||
init_late_loggers(&handler, tcx);
|
||||
if !tcx.crate_types().contains(&CrateType::Executable) {
|
||||
tcx.sess.fatal("miri only makes sense on bin crates");
|
||||
}
|
||||
|
46
tests/codegen/issues/issue-115385-llvm-jump-threading.rs
Normal file
46
tests/codegen/issues/issue-115385-llvm-jump-threading.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// compile-flags: -O -Ccodegen-units=1
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#[repr(i64)]
|
||||
pub enum Boolean {
|
||||
False = 0,
|
||||
True = 1,
|
||||
}
|
||||
|
||||
impl Clone for Boolean {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for Boolean {}
|
||||
|
||||
extern "C" {
|
||||
fn set_value(foo: *mut i64);
|
||||
fn bar();
|
||||
}
|
||||
|
||||
pub fn foo(x: bool) {
|
||||
let mut foo = core::mem::MaybeUninit::<i64>::uninit();
|
||||
unsafe {
|
||||
set_value(foo.as_mut_ptr());
|
||||
}
|
||||
|
||||
if x {
|
||||
let l1 = unsafe { *foo.as_mut_ptr().cast::<Boolean>() };
|
||||
if matches!(l1, Boolean::False) {
|
||||
unsafe {
|
||||
*foo.as_mut_ptr() = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let l2 = unsafe { *foo.as_mut_ptr() };
|
||||
if l2 == 2 {
|
||||
// CHECK: call void @bar
|
||||
unsafe {
|
||||
bar();
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ use rustc_interface::{Config, Queries};
|
||||
use rustc_middle::query::queries::mir_borrowck::ProvidedValue;
|
||||
use rustc_middle::query::{ExternProviders, Providers};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::{Session, EarlyErrorHandler};
|
||||
use rustc_session::Session;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::thread_local;
|
||||
@ -58,7 +58,6 @@ impl rustc_driver::Callbacks for CompilerCalls {
|
||||
// the result.
|
||||
fn after_analysis<'tcx>(
|
||||
&mut self,
|
||||
_handler: &EarlyErrorHandler,
|
||||
compiler: &Compiler,
|
||||
queries: &'tcx Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
|
9
tests/run-make/lto-linkage-used-attr/Makefile
Normal file
9
tests/run-make/lto-linkage-used-attr/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
include ../tools.mk
|
||||
|
||||
# Verify that the impl_* symbols are preserved. #108030
|
||||
# only-x86_64-unknown-linux-gnu
|
||||
# min-llvm-version: 17
|
||||
|
||||
all:
|
||||
$(RUSTC) -Cdebuginfo=0 -Copt-level=3 lib.rs
|
||||
$(RUSTC) -Clto=fat -Cdebuginfo=0 -Copt-level=3 main.rs
|
50
tests/run-make/lto-linkage-used-attr/lib.rs
Normal file
50
tests/run-make/lto-linkage-used-attr/lib.rs
Normal file
@ -0,0 +1,50 @@
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "cdylib"]
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! asm_func {
|
||||
($name:expr, $body:expr $(, $($args:tt)*)?) => {
|
||||
core::arch::global_asm!(
|
||||
concat!(
|
||||
".p2align 4\n",
|
||||
".hidden ", $name, "\n",
|
||||
".global ", $name, "\n",
|
||||
".type ", $name, ",@function\n",
|
||||
$name, ":\n",
|
||||
$body,
|
||||
".size ", $name, ",.-", $name,
|
||||
)
|
||||
$(, $($args)*)?
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! libcall_trampoline {
|
||||
($libcall:ident ; $libcall_impl:ident) => {
|
||||
asm_func!(
|
||||
stringify!($libcall),
|
||||
concat!(
|
||||
"
|
||||
.cfi_startproc simple
|
||||
.cfi_def_cfa_offset 0
|
||||
jmp {}
|
||||
.cfi_endproc
|
||||
",
|
||||
),
|
||||
sym $libcall_impl
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
pub mod trampolines {
|
||||
extern "C" {
|
||||
pub fn table_fill_funcref();
|
||||
pub fn table_fill_externref();
|
||||
}
|
||||
|
||||
unsafe extern "C" fn impl_table_fill_funcref() {}
|
||||
unsafe extern "C" fn impl_table_fill_externref() {}
|
||||
|
||||
libcall_trampoline!(table_fill_funcref ; impl_table_fill_funcref);
|
||||
libcall_trampoline!(table_fill_externref ; impl_table_fill_externref);
|
||||
}
|
10
tests/run-make/lto-linkage-used-attr/main.rs
Normal file
10
tests/run-make/lto-linkage-used-attr/main.rs
Normal file
@ -0,0 +1,10 @@
|
||||
extern crate lib;
|
||||
|
||||
use lib::trampolines::*;
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
table_fill_externref();
|
||||
table_fill_funcref();
|
||||
}
|
||||
}
|
@ -154,6 +154,10 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let foo_const = get_item(tcx, &items, (DefKind::Const, "FOO")).unwrap();
|
||||
// Ensure we don't panic trying to get the body of a constant.
|
||||
foo_const.body();
|
||||
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
@ -191,6 +195,8 @@ fn generate_input(path: &str) -> std::io::Result<()> {
|
||||
write!(
|
||||
file,
|
||||
r#"
|
||||
pub const FOO: u32 = 1 + 2;
|
||||
|
||||
fn generic<T, const U: usize>(t: T) -> [(); U] {{
|
||||
_ = t;
|
||||
[(); U]
|
||||
|
@ -106,6 +106,8 @@ test_transparent!(zst, Zst);
|
||||
test_transparent!(unit, ());
|
||||
test_transparent!(pair, (i32, f32)); // mixing in some floats since they often get special treatment
|
||||
test_transparent!(triple, (i8, i16, f32)); // chosen to fit into 64bit
|
||||
test_transparent!(triple_f32, (f32, f32, f32)); // homogeneous case
|
||||
test_transparent!(triple_f64, (f64, f64, f64));
|
||||
test_transparent!(tuple, (i32, f32, i64, f64));
|
||||
test_transparent!(empty_array, [u32; 0]);
|
||||
test_transparent!(empty_1zst_array, [u8; 0]);
|
||||
@ -113,14 +115,6 @@ test_transparent!(small_array, [i32; 2]); // chosen to fit into 64bit
|
||||
test_transparent!(large_array, [i32; 16]);
|
||||
test_transparent!(enum_, Option<i32>);
|
||||
test_transparent!(enum_niched, Option<&'static i32>);
|
||||
// Pure-float types that are not ScalarPair seem to be tricky.
|
||||
// FIXME: <https://github.com/rust-lang/rust/issues/115664>
|
||||
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
|
||||
mod tricky {
|
||||
use super::*;
|
||||
test_transparent!(triple_f32, (f32, f32, f32));
|
||||
test_transparent!(triple_f64, (f64, f64, f64));
|
||||
}
|
||||
|
||||
// RFC 3391 <https://rust-lang.github.io/rfcs/3391-result_ffi_guarantees.html>.
|
||||
macro_rules! test_nonnull {
|
||||
|
44
tests/ui/layout/homogeneous-aggr-transparent.rs
Normal file
44
tests/ui/layout/homogeneous-aggr-transparent.rs
Normal file
@ -0,0 +1,44 @@
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(transparent_unions)]
|
||||
use std::marker::PhantomData;
|
||||
|
||||
// Regression test for #115664. We want to ensure that `repr(transparent)` wrappers do not affect
|
||||
// the result of `homogeneous_aggregate`.
|
||||
|
||||
type Tuple = (f32, f32, f32);
|
||||
|
||||
struct Zst;
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Wrapper1<T>(T);
|
||||
#[repr(transparent)]
|
||||
struct Wrapper2<T>((), Zst, T);
|
||||
#[repr(transparent)]
|
||||
struct Wrapper3<T>(T, [u8; 0], PhantomData<u64>);
|
||||
#[repr(transparent)]
|
||||
union WrapperUnion<T: Copy> {
|
||||
nothing: (),
|
||||
something: T,
|
||||
}
|
||||
|
||||
#[rustc_layout(homogeneous_aggregate)]
|
||||
pub type Test0 = Tuple;
|
||||
//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
|
||||
#[rustc_layout(homogeneous_aggregate)]
|
||||
pub type Test1 = Wrapper1<Tuple>;
|
||||
//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
|
||||
#[rustc_layout(homogeneous_aggregate)]
|
||||
pub type Test2 = Wrapper2<Tuple>;
|
||||
//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
|
||||
#[rustc_layout(homogeneous_aggregate)]
|
||||
pub type Test3 = Wrapper3<Tuple>;
|
||||
//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
|
||||
#[rustc_layout(homogeneous_aggregate)]
|
||||
pub type Test4 = WrapperUnion<Tuple>;
|
||||
//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
|
||||
fn main() {}
|
32
tests/ui/layout/homogeneous-aggr-transparent.stderr
Normal file
32
tests/ui/layout/homogeneous-aggr-transparent.stderr
Normal file
@ -0,0 +1,32 @@
|
||||
error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
--> $DIR/homogeneous-aggr-transparent.rs:25:1
|
||||
|
|
||||
LL | pub type Test0 = Tuple;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
--> $DIR/homogeneous-aggr-transparent.rs:29:1
|
||||
|
|
||||
LL | pub type Test1 = Wrapper1<Tuple>;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
--> $DIR/homogeneous-aggr-transparent.rs:33:1
|
||||
|
|
||||
LL | pub type Test2 = Wrapper2<Tuple>;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
--> $DIR/homogeneous-aggr-transparent.rs:37:1
|
||||
|
|
||||
LL | pub type Test3 = Wrapper3<Tuple>;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
|
||||
--> $DIR/homogeneous-aggr-transparent.rs:41:1
|
||||
|
|
||||
LL | pub type Test4 = WrapperUnion<Tuple>;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
8
tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs
Normal file
8
tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#![deny(improper_ctypes_definitions)]
|
||||
|
||||
extern "C" fn foo<T: ?Sized + 'static>() -> Option<&'static T> {
|
||||
//~^ ERROR `extern` fn uses type `Option<&T>`, which is not FFI-safe
|
||||
None
|
||||
}
|
||||
|
||||
fn main() {}
|
16
tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr
Normal file
16
tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr
Normal file
@ -0,0 +1,16 @@
|
||||
error: `extern` fn uses type `Option<&T>`, which is not FFI-safe
|
||||
--> $DIR/lint-ctypes-option-nonnull-unsized.rs:3:45
|
||||
|
|
||||
LL | extern "C" fn foo<T: ?Sized + 'static>() -> Option<&'static T> {
|
||||
| ^^^^^^^^^^^^^^^^^^ not FFI-safe
|
||||
|
|
||||
= help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
|
||||
= note: enum has no representation hint
|
||||
note: the lint level is defined here
|
||||
--> $DIR/lint-ctypes-option-nonnull-unsized.rs:1:9
|
||||
|
|
||||
LL | #![deny(improper_ctypes_definitions)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
Reference in New Issue
Block a user