Compare commits

...
Sign in to create a new pull request.

1 commit

2 changed files with 75 additions and 18 deletions

View file

@ -2396,13 +2396,16 @@ impl ModuleBuilder {
#[track_caller]
pub fn extern_module_simulation_fn<
Args: fmt::Debug + Clone + Hash + Eq + Send + Sync + 'static,
F: Copy + Fn(Args, crate::sim::ExternModuleSimulationState) -> Fut + Send + Sync + 'static,
Fut: IntoFuture<Output = ()> + 'static,
>(
&self,
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));
}
}

View file

@ -54,7 +54,6 @@ use std::{
hash::Hash,
mem,
pin::{Pin, pin},
ptr,
rc::Rc,
sync::{Arc, Mutex, MutexGuard},
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;
}
pub struct SimGeneratorFn<Args, Fut> {
pub args: Args,
pub f: fn(Args, ExternModuleSimulationState) -> Fut,
/// Type requirements: `F` must be zero-sized, this guarantees that the function called
/// by `F` is entirely determined by the type `F` and not by its value, allowing us to
/// 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 {
// use for compile-time side-effect
let _ = Self::ASSERT_F_IS_ZERO_SIZED;
let Self { args, f: _ } = self;
f.debug_struct("SimGeneratorFn")
.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) {
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);
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 {
let Self { args, f } = self;
*args == other.args && ptr::fn_addr_eq(*f, other.f)
// use for compile-time side-effect
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 {
// use for compile-time side-effect
let _ = Self::ASSERT_F_IS_ZERO_SIZED;
Self {
args: self.args.clone(),
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<
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,
> ExternModuleSimGenerator for SimGeneratorFn<T, Fut>
> ExternModuleSimGenerator for SimGeneratorFn<Args, F, Fut>
{
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)
}
}