mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-01 07:13:24 +03:00
show span when there is an error invoking a global ctor/dtor or the thread main fn
This commit is contained in:
@@ -624,6 +624,7 @@ Definite bugs found:
|
||||
* [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite)
|
||||
* [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248)
|
||||
* [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816)
|
||||
* [`winit` registering a global constructor with the wrong ABI on Windows](https://github.com/rust-windowing/winit/issues/4435)
|
||||
|
||||
Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use rustc_target::spec::Os;
|
||||
|
||||
use crate::concurrency::GlobalDataRaceHandler;
|
||||
@@ -174,6 +174,10 @@ pub struct Thread<'tcx> {
|
||||
/// The virtual call stack.
|
||||
stack: Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>>,
|
||||
|
||||
/// A span that explains where the thread (or more specifically, its current root
|
||||
/// frame) "comes from".
|
||||
pub(crate) origin_span: Span,
|
||||
|
||||
/// The function to call when the stack ran empty, to figure out what to do next.
|
||||
/// Conceptually, this is the interpreter implementation of the things that happen 'after' the
|
||||
/// Rust language entry point for this thread returns (usually implemented by the C or OS runtime).
|
||||
@@ -303,6 +307,7 @@ fn new(name: Option<&str>, on_stack_empty: Option<StackEmptyCallback<'tcx>>) ->
|
||||
state: ThreadState::Enabled,
|
||||
thread_name: name.map(|name| Vec::from(name.as_bytes())),
|
||||
stack: Vec::new(),
|
||||
origin_span: DUMMY_SP,
|
||||
top_user_relevant_frame: None,
|
||||
join_status: ThreadJoinStatus::Joinable,
|
||||
unwind_payloads: Vec::new(),
|
||||
@@ -318,6 +323,7 @@ fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
unwind_payloads: panic_payload,
|
||||
last_error,
|
||||
stack,
|
||||
origin_span: _,
|
||||
top_user_relevant_frame: _,
|
||||
state: _,
|
||||
thread_name: _,
|
||||
@@ -584,6 +590,10 @@ pub fn active_thread_ref(&self) -> &Thread<'tcx> {
|
||||
&self.threads[self.active_thread]
|
||||
}
|
||||
|
||||
pub fn thread_ref(&self, thread_id: ThreadId) -> &Thread<'tcx> {
|
||||
&self.threads[thread_id]
|
||||
}
|
||||
|
||||
/// Mark the thread as detached, which means that no other thread will try
|
||||
/// to join it and the thread is responsible for cleaning up.
|
||||
///
|
||||
@@ -704,8 +714,9 @@ fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
|
||||
#[inline]
|
||||
fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
|
||||
let this = self.eval_context_mut();
|
||||
let mut callback = this
|
||||
.active_thread_mut()
|
||||
let active_thread = this.active_thread_mut();
|
||||
active_thread.origin_span = DUMMY_SP; // reset, the old value no longer applied
|
||||
let mut callback = active_thread
|
||||
.on_stack_empty
|
||||
.take()
|
||||
.expect("`on_stack_empty` not set up, or already running");
|
||||
@@ -891,11 +902,11 @@ fn start_regular_thread(
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
// Create the new thread
|
||||
let current_span = this.machine.current_user_relevant_span();
|
||||
let new_thread_id = this.machine.threads.create_thread({
|
||||
let mut state = tls::TlsDtorsState::default();
|
||||
Box::new(move |m| state.on_stack_empty(m))
|
||||
});
|
||||
let current_span = this.machine.current_user_relevant_span();
|
||||
match &mut this.machine.data_race {
|
||||
GlobalDataRaceHandler::None => {}
|
||||
GlobalDataRaceHandler::Vclocks(data_race) =>
|
||||
@@ -934,12 +945,12 @@ fn start_regular_thread(
|
||||
// it.
|
||||
let ret_place = this.allocate(ret_layout, MiriMemoryKind::Machine.into())?;
|
||||
|
||||
this.call_function(
|
||||
this.call_thread_root_function(
|
||||
instance,
|
||||
start_abi,
|
||||
&[func_arg],
|
||||
Some(&ret_place),
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
current_span,
|
||||
)?;
|
||||
|
||||
// Restore the old active thread frame.
|
||||
|
||||
@@ -444,7 +444,11 @@ pub fn report_result<'tcx>(
|
||||
write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap();
|
||||
|
||||
if labels.is_empty() {
|
||||
labels.push(format!("{} occurred here", title.unwrap_or("error")));
|
||||
labels.push(format!(
|
||||
"{} occurred {}",
|
||||
title.unwrap_or("error"),
|
||||
if stacktrace.is_empty() { "due to this code" } else { "here" }
|
||||
));
|
||||
}
|
||||
|
||||
report_msg(
|
||||
@@ -552,7 +556,14 @@ pub fn report_msg<'tcx>(
|
||||
thread: Option<ThreadId>,
|
||||
machine: &MiriMachine<'tcx>,
|
||||
) {
|
||||
let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
|
||||
let span = match stacktrace.first() {
|
||||
Some(fi) => fi.span,
|
||||
None =>
|
||||
match thread {
|
||||
Some(thread_id) => machine.threads.thread_ref(thread_id).origin_span,
|
||||
None => DUMMY_SP,
|
||||
},
|
||||
};
|
||||
let sess = machine.tcx.sess;
|
||||
let level = match diag_level {
|
||||
DiagLevel::Error => Level::Error,
|
||||
@@ -620,6 +631,12 @@ pub fn report_msg<'tcx>(
|
||||
err.note(format!("{frame_info} at {span}"));
|
||||
}
|
||||
}
|
||||
} else if stacktrace.len() == 0 && !span.is_dummy() {
|
||||
err.note(format!(
|
||||
"this {} occurred while pushing a call frame onto an empty stack",
|
||||
level.to_str()
|
||||
));
|
||||
err.note("the span indicates which code caused the function to be called, but may not be the literal call site");
|
||||
}
|
||||
|
||||
err.emit();
|
||||
|
||||
@@ -472,6 +472,22 @@ fn call_function(
|
||||
)
|
||||
}
|
||||
|
||||
/// Call a function in an "empty" thread.
|
||||
fn call_thread_root_function(
|
||||
&mut self,
|
||||
f: ty::Instance<'tcx>,
|
||||
caller_abi: ExternAbi,
|
||||
args: &[ImmTy<'tcx>],
|
||||
dest: Option<&MPlaceTy<'tcx>>,
|
||||
span: Span,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
assert!(this.active_thread_stack().is_empty());
|
||||
assert!(this.active_thread_ref().origin_span.is_dummy());
|
||||
this.active_thread_mut().origin_span = span;
|
||||
this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true })
|
||||
}
|
||||
|
||||
/// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter
|
||||
/// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`.
|
||||
/// The range is relative to `place`.
|
||||
@@ -995,11 +1011,12 @@ fn expect_target_feature_for_intrinsic(
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Lookup an array of immediates from any linker sections matching the provided predicate.
|
||||
/// Lookup an array of immediates from any linker sections matching the provided predicate,
|
||||
/// with the spans of where they were found.
|
||||
fn lookup_link_section(
|
||||
&mut self,
|
||||
include_name: impl Fn(&str) -> bool,
|
||||
) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
|
||||
) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
|
||||
let this = self.eval_context_mut();
|
||||
let tcx = this.tcx.tcx;
|
||||
|
||||
@@ -1012,6 +1029,7 @@ fn lookup_link_section(
|
||||
};
|
||||
if include_name(link_section.as_str()) {
|
||||
let instance = ty::Instance::mono(tcx, def_id);
|
||||
let span = tcx.def_span(def_id);
|
||||
let const_val = this.eval_global(instance).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
|
||||
@@ -1019,12 +1037,12 @@ fn lookup_link_section(
|
||||
});
|
||||
match const_val.layout.ty.kind() {
|
||||
ty::FnPtr(..) => {
|
||||
array.push(this.read_immediate(&const_val)?);
|
||||
array.push((this.read_immediate(&const_val)?, span));
|
||||
}
|
||||
ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
|
||||
let mut elems = this.project_array_fields(&const_val)?;
|
||||
while let Some((_idx, elem)) = elems.next(this)? {
|
||||
array.push(this.read_immediate(&elem)?);
|
||||
array.push((this.read_immediate(&elem)?, span));
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
clippy::needless_question_mark,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::too_long_first_doc_paragraph,
|
||||
clippy::len_zero,
|
||||
// We don't use translatable diagnostics
|
||||
rustc::diagnostic_outside_of_impl,
|
||||
// We are not implementing queries here so it's fine
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use std::task::Poll;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::BinaryFormat;
|
||||
|
||||
use crate::*;
|
||||
@@ -15,7 +16,7 @@ enum GlobalCtorStatePriv<'tcx> {
|
||||
#[default]
|
||||
Init,
|
||||
/// The list of constructor functions that we still have to call.
|
||||
Ctors(Vec<ImmTy<'tcx>>),
|
||||
Ctors(Vec<(ImmTy<'tcx>, Span)>),
|
||||
Done,
|
||||
}
|
||||
|
||||
@@ -67,19 +68,19 @@ pub fn on_stack_empty(
|
||||
break 'new_state Ctors(ctors);
|
||||
}
|
||||
Ctors(ctors) => {
|
||||
if let Some(ctor) = ctors.pop() {
|
||||
if let Some((ctor, span)) = ctors.pop() {
|
||||
let this = this.eval_context_mut();
|
||||
|
||||
let ctor = ctor.to_scalar().to_pointer(this)?;
|
||||
let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?;
|
||||
|
||||
// The signature of this function is `unsafe extern "C" fn()`.
|
||||
this.call_function(
|
||||
this.call_thread_root_function(
|
||||
thread_callback,
|
||||
ExternAbi::C { unwind: false },
|
||||
&[],
|
||||
None,
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
span,
|
||||
)?;
|
||||
|
||||
return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
use rustc_abi::{ExternAbi, HasDataLayout, Size};
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::Os;
|
||||
|
||||
use crate::*;
|
||||
@@ -17,7 +18,7 @@ pub struct TlsEntry<'tcx> {
|
||||
/// The data for this key. None is used to represent NULL.
|
||||
/// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.)
|
||||
data: BTreeMap<ThreadId, Scalar>,
|
||||
dtor: Option<ty::Instance<'tcx>>,
|
||||
dtor: Option<(ty::Instance<'tcx>, Span)>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
@@ -38,7 +39,7 @@ pub struct TlsData<'tcx> {
|
||||
|
||||
/// On macOS, each thread holds a list of destructor functions with their
|
||||
/// respective data arguments.
|
||||
macos_thread_dtors: BTreeMap<ThreadId, Vec<(ty::Instance<'tcx>, Scalar)>>,
|
||||
macos_thread_dtors: BTreeMap<ThreadId, Vec<(ty::Instance<'tcx>, Scalar, Span)>>,
|
||||
}
|
||||
|
||||
impl<'tcx> Default for TlsData<'tcx> {
|
||||
@@ -57,7 +58,7 @@ impl<'tcx> TlsData<'tcx> {
|
||||
#[expect(clippy::arithmetic_side_effects)]
|
||||
pub fn create_tls_key(
|
||||
&mut self,
|
||||
dtor: Option<ty::Instance<'tcx>>,
|
||||
dtor: Option<(ty::Instance<'tcx>, Span)>,
|
||||
max_size: Size,
|
||||
) -> InterpResult<'tcx, TlsKey> {
|
||||
let new_key = self.next_key;
|
||||
@@ -126,8 +127,9 @@ pub fn add_macos_thread_dtor(
|
||||
thread: ThreadId,
|
||||
dtor: ty::Instance<'tcx>,
|
||||
data: Scalar,
|
||||
span: Span,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.macos_thread_dtors.entry(thread).or_default().push((dtor, data));
|
||||
self.macos_thread_dtors.entry(thread).or_default().push((dtor, data, span));
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
@@ -154,7 +156,7 @@ fn fetch_tls_dtor(
|
||||
&mut self,
|
||||
key: Option<TlsKey>,
|
||||
thread_id: ThreadId,
|
||||
) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> {
|
||||
) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey, Span)> {
|
||||
use std::ops::Bound::*;
|
||||
|
||||
let thread_local = &mut self.keys;
|
||||
@@ -172,11 +174,10 @@ fn fetch_tls_dtor(
|
||||
for (&key, TlsEntry { data, dtor }) in thread_local.range_mut((start, Unbounded)) {
|
||||
match data.entry(thread_id) {
|
||||
BTreeEntry::Occupied(entry) => {
|
||||
if let Some(dtor) = dtor {
|
||||
if let Some((dtor, span)) = dtor {
|
||||
// Set TLS data to NULL, and call dtor with old value.
|
||||
let data_scalar = entry.remove();
|
||||
let ret = Some((*dtor, data_scalar, key));
|
||||
return ret;
|
||||
return Some((*dtor, data_scalar, key, *span));
|
||||
}
|
||||
}
|
||||
BTreeEntry::Vacant(_) => {}
|
||||
@@ -205,7 +206,7 @@ fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
for scalar in keys.values().flat_map(|v| v.data.values()) {
|
||||
scalar.visit_provenance(visit);
|
||||
}
|
||||
for (_, scalar) in macos_thread_dtors.values().flatten() {
|
||||
for (_, scalar, _) in macos_thread_dtors.values().flatten() {
|
||||
scalar.visit_provenance(visit);
|
||||
}
|
||||
}
|
||||
@@ -222,7 +223,7 @@ enum TlsDtorsStatePriv<'tcx> {
|
||||
PthreadDtors(RunningDtorState),
|
||||
/// For Windows Dtors, we store the list of functions that we still have to call.
|
||||
/// These are functions from the magic `.CRT$XLB` linker section.
|
||||
WindowsDtors(Vec<ImmTy<'tcx>>),
|
||||
WindowsDtors(Vec<(ImmTy<'tcx>, Span)>),
|
||||
Done,
|
||||
}
|
||||
|
||||
@@ -273,8 +274,8 @@ pub fn on_stack_empty(
|
||||
}
|
||||
}
|
||||
WindowsDtors(dtors) => {
|
||||
if let Some(dtor) = dtors.pop() {
|
||||
this.schedule_windows_tls_dtor(dtor)?;
|
||||
if let Some((dtor, span)) = dtors.pop() {
|
||||
this.schedule_windows_tls_dtor(dtor, span)?;
|
||||
return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter)
|
||||
} else {
|
||||
// No more destructors to run.
|
||||
@@ -297,7 +298,7 @@ impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
/// Schedule TLS destructors for Windows.
|
||||
/// On windows, TLS destructors are managed by std.
|
||||
fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
|
||||
fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
// Windows has a special magic linker section that is run on certain events.
|
||||
@@ -305,7 +306,7 @@ fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
|
||||
interp_ok(this.lookup_link_section(|section| section == ".CRT$XLB")?)
|
||||
}
|
||||
|
||||
fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> {
|
||||
fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>, span: Span) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let dtor = dtor.to_scalar().to_pointer(this)?;
|
||||
@@ -320,12 +321,12 @@ fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx>
|
||||
// The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.
|
||||
// FIXME: `h` should be a handle to the current module and what `pv` should be is unknown
|
||||
// but both are ignored by std.
|
||||
this.call_function(
|
||||
this.call_thread_root_function(
|
||||
thread_callback,
|
||||
ExternAbi::System { unwind: false },
|
||||
&[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr],
|
||||
None,
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
span,
|
||||
)?;
|
||||
interp_ok(())
|
||||
}
|
||||
@@ -338,15 +339,15 @@ fn schedule_macos_tls_dtor(&mut self) -> InterpResult<'tcx, Poll<()>> {
|
||||
// registers another destructor, it will be run next.
|
||||
// See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277
|
||||
let dtor = this.machine.tls.macos_thread_dtors.get_mut(&thread_id).and_then(Vec::pop);
|
||||
if let Some((instance, data)) = dtor {
|
||||
if let Some((instance, data, span)) = dtor {
|
||||
trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id);
|
||||
|
||||
this.call_function(
|
||||
this.call_thread_root_function(
|
||||
instance,
|
||||
ExternAbi::C { unwind: false },
|
||||
&[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)],
|
||||
None,
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
span,
|
||||
)?;
|
||||
|
||||
return interp_ok(Poll::Pending);
|
||||
@@ -370,7 +371,7 @@ fn schedule_next_pthread_tls_dtor(
|
||||
// We ran each dtor once, start over from the beginning.
|
||||
None => this.machine.tls.fetch_tls_dtor(None, active_thread),
|
||||
};
|
||||
if let Some((instance, ptr, key)) = dtor {
|
||||
if let Some((instance, ptr, key, span)) = dtor {
|
||||
state.last_key = Some(key);
|
||||
trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread);
|
||||
assert!(
|
||||
@@ -378,12 +379,12 @@ fn schedule_next_pthread_tls_dtor(
|
||||
"data can't be NULL when dtor is called!"
|
||||
);
|
||||
|
||||
this.call_function(
|
||||
this.call_thread_root_function(
|
||||
instance,
|
||||
ExternAbi::C { unwind: false },
|
||||
&[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
|
||||
None,
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
span,
|
||||
)?;
|
||||
|
||||
return interp_ok(Poll::Pending);
|
||||
|
||||
@@ -634,7 +634,10 @@ fn emulate_foreign_item_inner(
|
||||
|
||||
// Extract the function type out of the signature (that seems easier than constructing it ourselves).
|
||||
let dtor = if !this.ptr_is_null(dtor)? {
|
||||
Some(this.get_ptr_fn(dtor)?.as_instance()?)
|
||||
Some((
|
||||
this.get_ptr_fn(dtor)?.as_instance()?,
|
||||
this.machine.current_user_relevant_span(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@@ -158,7 +158,12 @@ fn emulate_foreign_item_inner(
|
||||
let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
|
||||
let data = this.read_scalar(data)?;
|
||||
let active_thread = this.active_thread();
|
||||
this.machine.tls.add_macos_thread_dtor(active_thread, dtor, data)?;
|
||||
this.machine.tls.add_macos_thread_dtor(
|
||||
active_thread,
|
||||
dtor,
|
||||
data,
|
||||
this.machine.current_user_relevant_span(),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Querying system information
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//@ignore-target: windows # No pthreads on Windows
|
||||
//~^ERROR: calling a function with more arguments than it expected
|
||||
|
||||
//! The thread function must have exactly one argument.
|
||||
|
||||
@@ -17,6 +16,7 @@ fn main() {
|
||||
mem::transmute(thread_start);
|
||||
assert_eq!(
|
||||
libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
//~^ERROR: calling a function with more arguments than it expected
|
||||
0
|
||||
);
|
||||
assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
error: Undefined Behavior: calling a function with more arguments than it expected
|
||||
--> tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs:LL:CC
|
||||
|
|
||||
LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
|
||||
|
|
||||
= note: Undefined Behavior occurred here
|
||||
= note: (no span available)
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: this error occurred while pushing a call frame onto an empty stack
|
||||
= note: the span indicates which code caused the function to be called, but may not be the literal call site
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//@ignore-target: windows # No pthreads on Windows
|
||||
//~^ERROR: calling a function with fewer arguments than it requires
|
||||
|
||||
//! The thread function must have exactly one argument.
|
||||
|
||||
@@ -17,6 +16,7 @@ fn main() {
|
||||
mem::transmute(thread_start);
|
||||
assert_eq!(
|
||||
libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
//~^ERROR: calling a function with fewer arguments than it requires
|
||||
0
|
||||
);
|
||||
assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
error: Undefined Behavior: calling a function with fewer arguments than it requires
|
||||
--> tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs:LL:CC
|
||||
|
|
||||
LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
|
||||
|
|
||||
= note: Undefined Behavior occurred here
|
||||
= note: (no span available)
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: this error occurred while pushing a call frame onto an empty stack
|
||||
= note: the span indicates which code caused the function to be called, but may not be the literal call site
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//@ignore-target: windows # No pthreads on Windows
|
||||
|
||||
use std::{mem, ptr};
|
||||
|
||||
pub type Key = libc::pthread_key_t;
|
||||
|
||||
pub unsafe fn create(dtor: unsafe fn(*mut u8)) -> Key {
|
||||
let mut key = 0;
|
||||
assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
|
||||
//~^ERROR: calling a function with calling convention "Rust"
|
||||
key
|
||||
}
|
||||
|
||||
unsafe fn dtor(_ptr: *mut u8) {}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let key = create(dtor);
|
||||
libc::pthread_setspecific(key, ptr::without_provenance(1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C"
|
||||
--> tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs:LL:CC
|
||||
|
|
||||
LL | assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: this error occurred while pushing a call frame onto an empty stack
|
||||
= note: the span indicates which code caused the function to be called, but may not be the literal call site
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
unsafe extern "C" fn ctor() -> i32 {
|
||||
//~^ERROR: calling a function with return type i32 passing return place of type ()
|
||||
0
|
||||
}
|
||||
|
||||
@@ -31,6 +30,7 @@ macro_rules! ctor {
|
||||
)]
|
||||
#[used]
|
||||
static $ident: unsafe extern "C" fn() -> i32 = $ctor;
|
||||
//~^ERROR: calling a function with return type i32 passing return place of type ()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
error: Undefined Behavior: calling a function with return type i32 passing return place of type ()
|
||||
--> tests/fail/shims/ctor_wrong_ret_type.rs:LL:CC
|
||||
|
|
||||
LL | static $ident: unsafe extern "C" fn() -> i32 = $ctor;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
|
||||
...
|
||||
LL | ctor! { CTOR = ctor }
|
||||
| --------------------- in this macro invocation
|
||||
|
|
||||
= note: Undefined Behavior occurred here
|
||||
= note: (no span available)
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: this error occurred while pushing a call frame onto an empty stack
|
||||
= note: the span indicates which code caused the function to be called, but may not be the literal call site
|
||||
= note: this error originates in the macro `ctor` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//@only-target: darwin
|
||||
|
||||
use std::{mem, ptr};
|
||||
|
||||
extern "C" {
|
||||
fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
|
||||
}
|
||||
|
||||
fn register(dtor: unsafe fn(*mut u8)) {
|
||||
unsafe {
|
||||
_tlv_atexit(mem::transmute(dtor), ptr::null_mut());
|
||||
//~^ERROR: calling a function with calling convention "Rust"
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
register(|_| ());
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C"
|
||||
--> tests/fail/shims/macos_tlv_atexit_wrong_abi.rs:LL:CC
|
||||
|
|
||||
LL | _tlv_atexit(mem::transmute(dtor), ptr::null_mut());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: this error occurred while pushing a call frame onto an empty stack
|
||||
= note: the span indicates which code caused the function to be called, but may not be the literal call site
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Reference in New Issue
Block a user