don't compare function pointers -- they're non-deterministic #60
2 changed files with 75 additions and 18 deletions
|
|
@ -2396,13 +2396,16 @@ impl ModuleBuilder {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn extern_module_simulation_fn<
|
pub fn extern_module_simulation_fn<
|
||||||
Args: fmt::Debug + Clone + Hash + Eq + Send + Sync + 'static,
|
Args: fmt::Debug + Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
F: Copy + Fn(Args, crate::sim::ExternModuleSimulationState) -> Fut + Send + Sync + 'static,
|
||||||
Fut: IntoFuture<Output = ()> + 'static,
|
Fut: IntoFuture<Output = ()> + 'static,
|
||||||
>(
|
>(
|
||||||
&self,
|
&self,
|
||||||
args: Args,
|
args: Args,
|
||||||
f: fn(Args, crate::sim::ExternModuleSimulationState) -> Fut,
|
f: F,
|
||||||
) {
|
) {
|
||||||
self.extern_module_simulation(crate::sim::SimGeneratorFn { args, f });
|
// use for compile-time side-effect
|
||||||
|
let _ = crate::sim::SimGeneratorFn::<Args, F, Fut>::ASSERT_F_IS_ZERO_SIZED;
|
||||||
|
self.extern_module_simulation(crate::sim::SimGeneratorFn::new(args, f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ use std::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
mem,
|
mem,
|
||||||
pin::{Pin, pin},
|
pin::{Pin, pin},
|
||||||
ptr,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, Mutex, MutexGuard},
|
sync::{Arc, Mutex, MutexGuard},
|
||||||
task::Poll,
|
task::Poll,
|
||||||
|
|
@ -4065,13 +4064,48 @@ pub trait ExternModuleSimGenerator: Clone + Eq + Hash + Any + Send + Sync + fmt:
|
||||||
fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture<Output = ()> + 'a;
|
fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture<Output = ()> + 'a;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SimGeneratorFn<Args, Fut> {
|
/// Type requirements: `F` must be zero-sized, this guarantees that the function called
|
||||||
pub args: Args,
|
/// by `F` is entirely determined by the type `F` and not by its value, allowing us to
|
||||||
pub f: fn(Args, ExternModuleSimulationState) -> Fut,
|
/// correctly leave `F` out of the stuff used for `Hash` and `Eq`.
|
||||||
|
///
|
||||||
|
/// Note we can't just use function pointers instead -- comparing them is non-deterministic
|
||||||
|
/// since Rust will duplicate and/or merge functions, even if those functions happen to have
|
||||||
|
/// different UB but just happen to compile to the same assembly language:
|
||||||
|
/// <https://github.com/rust-lang/unsafe-code-guidelines/issues/589#issuecomment-3515424930>
|
||||||
|
pub struct SimGeneratorFn<Args, F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut, Fut> {
|
||||||
|
args: Args,
|
||||||
|
f: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Args: fmt::Debug, Fut> fmt::Debug for SimGeneratorFn<Args, Fut> {
|
impl<Args, F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut, Fut>
|
||||||
|
SimGeneratorFn<Args, F, Fut>
|
||||||
|
{
|
||||||
|
pub const ASSERT_F_IS_ZERO_SIZED: () = {
|
||||||
|
if size_of::<F>() != 0 {
|
||||||
|
panic!(
|
||||||
|
"F must be zero-sized -- so it must be a closure with no captures or a function item, it can't be a function pointer"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const fn new(args: Args, f: F) -> Self {
|
||||||
|
// use for compile-time side-effect
|
||||||
|
let _ = Self::ASSERT_F_IS_ZERO_SIZED;
|
||||||
|
Self { args, f }
|
||||||
|
}
|
||||||
|
pub const fn args(&self) -> &Args {
|
||||||
|
&self.args
|
||||||
|
}
|
||||||
|
pub const fn f(&self) -> F {
|
||||||
|
self.f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Args: fmt::Debug, F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut, Fut> fmt::Debug
|
||||||
|
for SimGeneratorFn<Args, F, Fut>
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
// use for compile-time side-effect
|
||||||
|
let _ = Self::ASSERT_F_IS_ZERO_SIZED;
|
||||||
let Self { args, f: _ } = self;
|
let Self { args, f: _ } = self;
|
||||||
f.debug_struct("SimGeneratorFn")
|
f.debug_struct("SimGeneratorFn")
|
||||||
.field("args", args)
|
.field("args", args)
|
||||||
|
|
@ -4080,25 +4114,39 @@ impl<Args: fmt::Debug, Fut> fmt::Debug for SimGeneratorFn<Args, Fut> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Args: Hash, Fut> Hash for SimGeneratorFn<Args, Fut> {
|
impl<Args: Hash, F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut, Fut> Hash
|
||||||
|
for SimGeneratorFn<Args, F, Fut>
|
||||||
|
{
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
let Self { args, f } = self;
|
// use for compile-time side-effect
|
||||||
|
let _ = Self::ASSERT_F_IS_ZERO_SIZED;
|
||||||
|
let Self { args, f: _ } = self;
|
||||||
args.hash(state);
|
args.hash(state);
|
||||||
f.hash(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Args: Eq, Fut> Eq for SimGeneratorFn<Args, Fut> {}
|
impl<Args: Eq, F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut, Fut> Eq
|
||||||
|
for SimGeneratorFn<Args, F, Fut>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
impl<Args: PartialEq, Fut> PartialEq for SimGeneratorFn<Args, Fut> {
|
impl<Args: PartialEq, F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut, Fut> PartialEq
|
||||||
|
for SimGeneratorFn<Args, F, Fut>
|
||||||
|
{
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
let Self { args, f } = self;
|
// use for compile-time side-effect
|
||||||
*args == other.args && ptr::fn_addr_eq(*f, other.f)
|
let _ = Self::ASSERT_F_IS_ZERO_SIZED;
|
||||||
|
let Self { args, f: _ } = self;
|
||||||
|
*args == other.args
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Args: Clone, Fut> Clone for SimGeneratorFn<Args, Fut> {
|
impl<Args: Clone, F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut, Fut> Clone
|
||||||
|
for SimGeneratorFn<Args, F, Fut>
|
||||||
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
|
// use for compile-time side-effect
|
||||||
|
let _ = Self::ASSERT_F_IS_ZERO_SIZED;
|
||||||
Self {
|
Self {
|
||||||
args: self.args.clone(),
|
args: self.args.clone(),
|
||||||
f: self.f,
|
f: self.f,
|
||||||
|
|
@ -4106,14 +4154,20 @@ impl<Args: Clone, Fut> Clone for SimGeneratorFn<Args, Fut> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Args: Copy, Fut> Copy for SimGeneratorFn<Args, Fut> {}
|
impl<Args: Copy, F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut, Fut> Copy
|
||||||
|
for SimGeneratorFn<Args, F, Fut>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static,
|
Args: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static,
|
||||||
|
F: Copy + Fn(Args, ExternModuleSimulationState) -> Fut + Send + Sync + 'static,
|
||||||
Fut: IntoFuture<Output = ()> + 'static,
|
Fut: IntoFuture<Output = ()> + 'static,
|
||||||
> ExternModuleSimGenerator for SimGeneratorFn<T, Fut>
|
> ExternModuleSimGenerator for SimGeneratorFn<Args, F, Fut>
|
||||||
{
|
{
|
||||||
fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture<Output = ()> + 'a {
|
fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture<Output = ()> + 'a {
|
||||||
|
// use for compile-time side-effect
|
||||||
|
let _ = Self::ASSERT_F_IS_ZERO_SIZED;
|
||||||
(self.f)(self.args.clone(), sim)
|
(self.f)(self.args.clone(), sim)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue