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:
bors 2023-09-11 20:53:42 +00:00
commit b4e54c6e39
25 changed files with 448 additions and 202 deletions

View File

@ -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;

View 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()
}
})
}
}

View File

@ -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();
}

View File

@ -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.

View File

@ -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 }

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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");
}

View 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();
}
}
}

View File

@ -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 {

View 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

View 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);
}

View File

@ -0,0 +1,10 @@
extern crate lib;
use lib::trampolines::*;
fn main() {
unsafe {
table_fill_externref();
table_fill_funcref();
}
}

View File

@ -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]

View File

@ -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 {

View 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() {}

View 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

View 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() {}

View 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