Skip to main content

wasm_bindgen/convert/
closures.rs

1use alloc::boxed::Box;
2use core::mem;
3use core::panic::AssertUnwindSafe;
4
5use crate::__rt::marker::ErasableGeneric;
6use crate::__rt::maybe_catch_unwind;
7use crate::__rt::WasmWord;
8use crate::closure::{
9    Closure, IntoWasmClosure, IntoWasmClosureRef, IntoWasmClosureRefMut, ScopedClosure,
10    WasmClosure, WasmClosureFnOnce, WasmClosureFnOnceAbort,
11};
12use crate::convert::slices::WasmSlice;
13use crate::convert::traits::UpcastFrom;
14use crate::convert::RefFromWasmAbi;
15use crate::convert::{FromWasmAbi, IntoWasmAbi, ReturnWasmAbi, WasmAbi, WasmRet};
16use crate::describe::{inform, WasmDescribe, FUNCTION};
17use crate::sys::Undefined;
18use crate::throw_str;
19use crate::JsValue;
20
21#[inline]
22fn release<T>(_: T) {}
23
24macro_rules! closures {
25    // Unwind safe passing
26    ([$($maybe_unwind_safe:tt)*] $($rest:tt)*) => {
27        closures!(@process [$($maybe_unwind_safe)*] $($rest)*);
28    };
29
30    // One-arity recurse
31    (@process [$($unwind_safe:tt)*] ($($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) $($rest:tt)*) => {
32        closures!(@impl_for_args ($($var),*) FromWasmAbi [$($unwind_safe)*] $(try_from_abi($var) => $var $arg1 $arg2 $arg3 $arg4)*);
33        closures!(@process [$($unwind_safe)*] $($rest)*);
34    };
35
36    // Base case
37    (@process [$($unwind_safe:tt)*]) => {};
38
39    // A counter helper to count number of arguments.
40    (@count_one $ty:ty) => (1);
41
42    // Takes each argument's error, dropping the converted ones in reverse.
43    (@errs) => {};
44    (@errs $first:ident $($rest:ident)*) => {
45        closures!(@errs $($rest)*);
46        let $first = $first.err();
47    };
48
49    // Drops each argument in reverse.
50    (@release) => {};
51    (@release $first:ident $($rest:ident)*) => {
52        closures!(@release $($rest)*);
53        release($first);
54    };
55
56    (@describe ( $($ty:ty),* )) => {
57        // Needs to be a constant so that interpreter doesn't crash on
58        // unsupported operations in debug mode.
59        const ARG_COUNT: u32 = 0 $(+ closures!(@count_one $ty))*;
60        inform(ARG_COUNT);
61        $(<$ty>::describe();)*
62    };
63
64    // This silly helper is because by default Rust infers `|var_with_ref_type| ...` closure
65    // as `impl Fn(&'outer_lifetime A)` instead of `impl for<'temp_lifetime> Fn(&'temp_lifetime A)`
66    // while `|var_with_ref_type: &A|` makes it use the higher-order generic as expected.
67    (@closure ($($ty:ty),*) $($var:ident)* $body:block) => (move |$($var: $ty),*| $body);
68
69    (@impl_for_fn $is_mut:literal [$($mut:ident)?] $Fn:ident $FnArgs:tt $FromWasmAbi:ident $($try_fn:ident ($use_expr:expr) => $var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) => (const _: () = {
70        impl<$($var,)* R> IntoWasmAbi for &'_ $($mut)? (dyn $Fn $FnArgs -> R + '_)
71        where
72            Self: WasmDescribe,
73        {
74            type Abi = WasmSlice;
75
76            fn into_abi(self) -> WasmSlice {
77                unsafe {
78                    let (a, b): (usize, usize) = mem::transmute(self);
79                    WasmSlice::from_usize(a, b)
80                }
81            }
82        }
83
84        unsafe impl<'a, $($var,)* R> ErasableGeneric for &'a $($mut)? (dyn $Fn $FnArgs -> R + 'a)
85        where
86            $($var: ErasableGeneric,)*
87            R: ErasableGeneric
88        {
89            type Repr = &'a (dyn $Fn ($(<$var as ErasableGeneric>::Repr,)*) -> <R as ErasableGeneric>::Repr + 'a);
90        }
91
92        // Invoke shim for closures. The const generic `UNWIND_SAFE` controls
93        // whether panics are caught and converted to JS exceptions (`true`) or
94        // left to unwind/abort (`false`). When `panic=unwind` is not available,
95        // `UNWIND_SAFE` has no effect — panics always abort.
96        #[allow(non_snake_case)]
97        unsafe extern "C-unwind" fn invoke<$($var: $FromWasmAbi,)* R: ReturnWasmAbi, const UNWIND_SAFE: bool>(
98            a: WasmWord,
99            b: WasmWord,
100            $(
101            $arg1: <$var::Abi as WasmAbi>::Prim1,
102            $arg2: <$var::Abi as WasmAbi>::Prim2,
103            $arg3: <$var::Abi as WasmAbi>::Prim3,
104            $arg4: <$var::Abi as WasmAbi>::Prim4,
105            )*
106        ) -> WasmRet<R::Abi> {
107            // A throw runs no destructors, so every argument is converted
108            // and released again before anything is thrown.
109            $(
110                let $var = $var::$try_fn($var::Abi::join($arg1, $arg2, $arg3, $arg4));
111            )*
112            let ($($var,)*) = match ($($var,)*) {
113                ($(Ok($var),)*) if !a.is_zero() => ($($var,)*),
114                ($($var,)*) => {
115                    closures!(@errs $($var)*);
116                    throw_str(if a.is_zero() {
117                        "closure invoked recursively or after being dropped"
118                    } else {
119                        None $(.or($var))*.unwrap_or_default()
120                    })
121                }
122            };
123            let ret = {
124                let f: & $($mut)? dyn $Fn $FnArgs -> R =
125                    mem::transmute((a.into_usize(), b.into_usize()));
126                if UNWIND_SAFE {
127                    maybe_catch_unwind(AssertUnwindSafe(|| f($($use_expr),*)))
128                } else {
129                    f($($use_expr),*)
130                }
131            };
132            ret.return_abi().into()
133        }
134
135        #[allow(clippy::fn_to_numeric_cast)]
136        impl<$($var,)* R> WasmDescribe for dyn $Fn $FnArgs -> R + '_
137        where
138            $($var: $FromWasmAbi,)*
139            R: ReturnWasmAbi,
140        {
141            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
142            fn describe() {
143                // Raw &dyn Fn/&dyn FnMut passed as arguments use the catching
144                // invoke shim by default, matching the previous runtime behavior.
145                <Self as WasmClosure>::describe_invoke::<true>();
146            }
147        }
148
149        unsafe impl<'__closure, $($var,)* R> WasmClosure for dyn $Fn $FnArgs -> R + '__closure
150        where
151            $($var: $FromWasmAbi,)*
152            R: ReturnWasmAbi,
153        {
154            const IS_MUT: bool = $is_mut;
155            type Static = dyn $Fn $FnArgs -> R;
156            type AsMut = dyn FnMut $FnArgs -> R + '__closure;
157            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
158            fn describe_invoke<const UNWIND_SAFE: bool>() {
159                inform(FUNCTION);
160                inform(invoke::<$($var,)* R, UNWIND_SAFE> as *const () as usize as u32);
161                closures!(@describe $FnArgs);
162                R::describe();
163                R::describe();
164            }
165        }
166
167        impl<T, $($var,)* R> IntoWasmClosure<dyn $Fn $FnArgs -> R> for T
168        where
169            T: 'static + $Fn $FnArgs -> R,
170        {
171            fn unsize(self: Box<Self>) -> Box<dyn $Fn $FnArgs -> R> { self }
172        }
173    };);
174
175    // IntoWasmClosureRef is only implemented for Fn, not FnMut.
176    // IntoWasmClosureRefMut is implemented for FnMut.
177    // Since Fn: FnMut, any Fn closure can be used as FnMut, so this covers all cases.
178    (@impl_unsize_closure_ref $FnArgs:tt $FromWasmAbi:ident $($try_fn:ident ($use_expr:expr) => $var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) => (
179        impl<'a, T: 'a, $($var: 'a + $FromWasmAbi,)* R: 'a + ReturnWasmAbi> IntoWasmClosureRef<dyn Fn $FnArgs -> R + 'a> for T
180        where
181            T: Fn $FnArgs -> R,
182        {
183            fn unsize_closure_ref(&self) -> &(dyn Fn $FnArgs -> R + 'a) { self }
184        }
185
186        impl<'a, T: 'a, $($var: 'a + $FromWasmAbi,)* R: 'a + ReturnWasmAbi> IntoWasmClosureRefMut<dyn FnMut $FnArgs -> R + 'a> for T
187        where
188            T: FnMut $FnArgs -> R,
189        {
190            fn unsize_closure_ref(&mut self) -> &mut (dyn FnMut $FnArgs -> R + 'a) { self }
191        }
192    );
193
194    (@impl_for_args $FnArgs:tt $FromWasmAbi:ident [$($maybe_unwind_safe:tt)*] $($try_fn:ident ($use_expr:expr) => $var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) => {
195        closures!(@impl_for_fn false [] Fn $FnArgs $FromWasmAbi $($try_fn ($use_expr) => $var $arg1 $arg2 $arg3 $arg4)*);
196        closures!(@impl_for_fn true [mut] FnMut $FnArgs $FromWasmAbi $($try_fn ($use_expr) => $var $arg1 $arg2 $arg3 $arg4)*);
197        closures!(@impl_unsize_closure_ref $FnArgs $FromWasmAbi $($try_fn ($use_expr) => $var $arg1 $arg2 $arg3 $arg4)*);
198
199        // The memory safety here in these implementations below is a bit tricky. We
200        // want to be able to drop the `Closure` object from within the invocation of a
201        // `Closure` for cases like promises. That means that while it's running we
202        // might drop the `Closure`, but that shouldn't invalidate the environment yet.
203        //
204        // Instead what we do is to wrap closures in `Rc` variables. The main `Closure`
205        // has a strong reference count which keeps the trait object alive. Each
206        // invocation of a closure then *also* clones this and gets a new reference
207        // count. When the closure returns it will release the reference count.
208        //
209        // This means that if the main `Closure` is dropped while it's being invoked
210        // then destruction is deferred until execution returns. Otherwise it'll
211        // deallocate data immediately.
212
213        #[allow(non_snake_case, unused_parens)]
214        impl<T, $($var,)* R> WasmClosureFnOnce<dyn FnMut $FnArgs -> R, $FnArgs, R> for T
215        where
216            T: 'static + (FnOnce $FnArgs -> R),
217            $($var: $FromWasmAbi + 'static,)*
218            R: ReturnWasmAbi + 'static,
219            $($maybe_unwind_safe)*
220        {
221            fn into_fn_mut(self) -> Box<dyn FnMut $FnArgs -> R> {
222                let mut me = Some(self);
223                Box::new(move |$($var),*| {
224                    match me.take() {
225                        Some(me) => me($($var),*),
226                        None => {
227                            closures!(@release $($var)*);
228                            throw_str("FnOnce called more than once")
229                        }
230                    }
231                })
232            }
233
234            fn into_js_function(self) -> JsValue {
235                use alloc::rc::Rc;
236                use crate::__rt::WasmRefCell;
237
238                let rc1 = Rc::new(WasmRefCell::new(None));
239                let rc2 = rc1.clone();
240
241                let closure = Closure::once(closures!(@closure $FnArgs $($var)* {
242                    let result = self($($var),*);
243
244                    // And then drop the `Rc` holding this function's `Closure`
245                    // alive.
246                    debug_assert_eq!(Rc::strong_count(&rc2), 1);
247                    let option_closure = rc2.borrow_mut().take();
248                    debug_assert!(option_closure.is_some());
249                    drop(option_closure);
250
251                    result
252                }));
253
254                let js_val = closure.as_ref().clone();
255
256                *rc1.borrow_mut() = Some(closure);
257                debug_assert_eq!(Rc::strong_count(&rc1), 2);
258                drop(rc1);
259
260                js_val
261            }
262        }
263
264        #[allow(non_snake_case, unused_parens)]
265        impl<T, $($var,)* R> WasmClosureFnOnceAbort<dyn FnMut $FnArgs -> R, $FnArgs, R> for T
266        where
267            T: 'static + (FnOnce $FnArgs -> R),
268            $($var: $FromWasmAbi + 'static,)*
269            R: ReturnWasmAbi + 'static,
270        {
271            fn into_fn_mut(self) -> Box<dyn FnMut $FnArgs -> R> {
272                let mut me = Some(self);
273                Box::new(move |$($var),*| {
274                    match me.take() {
275                        Some(me) => me($($var),*),
276                        None => {
277                            closures!(@release $($var)*);
278                            throw_str("FnOnce called more than once")
279                        }
280                    }
281                })
282            }
283
284            fn into_js_function(self) -> JsValue {
285                use alloc::rc::Rc;
286                use crate::__rt::WasmRefCell;
287
288                let rc1 = Rc::new(WasmRefCell::new(None));
289                let rc2 = rc1.clone();
290
291                // TODO: Unwind safety for FnOnce
292                let closure = Closure::once_aborting(closures!(@closure $FnArgs $($var)* {
293                    let result = self($($var),*);
294
295                    // And then drop the `Rc` holding this function's `Closure`
296                    // alive.
297                    debug_assert_eq!(Rc::strong_count(&rc2), 1);
298                    let option_closure = rc2.borrow_mut().take();
299                    debug_assert!(option_closure.is_some());
300                    drop(option_closure);
301
302                    result
303                }));
304
305                let js_val = closure.as_ref().clone();
306
307                *rc1.borrow_mut() = Some(closure);
308                debug_assert_eq!(Rc::strong_count(&rc1), 2);
309                drop(rc1);
310
311                js_val
312            }
313        }
314    };
315
316    ([$($unwind_safe:tt)*] $( ($($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) )*) => ($(
317        closures!(@impl_for_args ($($var),*) FromWasmAbi [$($maybe_unwind_safe)*] $(try_from_abi($var) => $var $arg1 $arg2 $arg3 $arg4)*);
318    )*);
319}
320
321#[cfg(all(feature = "std", target_arch = "wasm32", panic = "unwind"))]
322closures! {
323    [T: core::panic::UnwindSafe,]
324    ()
325    (A a1 a2 a3 a4)
326    (A a1 a2 a3 a4 B b1 b2 b3 b4)
327    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
328    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
329    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4)
330    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4)
331    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4)
332    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4 H h1 h2 h3 h4)
333}
334
335#[cfg(not(all(feature = "std", target_arch = "wasm32", panic = "unwind")))]
336closures! {
337    []
338    ()
339    (A a1 a2 a3 a4)
340    (A a1 a2 a3 a4 B b1 b2 b3 b4)
341    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
342    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
343    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4)
344    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4)
345    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4)
346    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4 H h1 h2 h3 h4)
347}
348
349// Comprehensive type-safe cross-function covariant and contravariant casting rules
350macro_rules! impl_fn_upcasts {
351    () => {
352        impl_fn_upcasts!(@arities
353            [0 []]
354            [1 [A1 B1] O1]
355            [2 [A1 B1 A2 B2] O2]
356            [3 [A1 B1 A2 B2 A3 B3] O3]
357            [4 [A1 B1 A2 B2 A3 B3 A4 B4] O4]
358            [5 [A1 B1 A2 B2 A3 B3 A4 B4 A5 B5] O5]
359            [6 [A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 A6 B6] O6]
360            [7 [A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 A6 B6 A7 B7] O7]
361            [8 [A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 A6 B6 A7 B7 A8 B8] O8]
362        );
363    };
364
365    (@arities) => {};
366
367    (@arities [$n:tt $args:tt $($opt:ident)?] $([$rest_n:tt $rest_args:tt $($rest_opt:ident)?])*) => {
368        impl_fn_upcasts!(@same $args);
369        impl_fn_upcasts!(@cross_all $args [] $([$rest_n $rest_args $($rest_opt)?])*);
370        impl_fn_upcasts!(@arities $([$rest_n $rest_args $($rest_opt)?])*);
371    };
372
373    (@same []) => {
374        impl<R1, R2> UpcastFrom<fn() -> R1> for fn() -> R2
375        where
376            R2: UpcastFrom<R1>
377        {}
378
379        impl<'a, R1, R2> UpcastFrom<dyn Fn() -> R1 + 'a> for dyn Fn() -> R2 + 'a
380        where
381            R2: UpcastFrom<R1>
382        {}
383
384        impl<'a, R1, R2> UpcastFrom<dyn FnMut() -> R1 + 'a> for dyn FnMut() -> R2 + 'a
385        where
386            R2: UpcastFrom<R1>
387        {}
388    };
389
390    // Arguments implemented with contravariance
391    (@same [$($A1:ident $A2:ident)+]) => {
392        impl<R1, R2, $($A1, $A2),+> UpcastFrom<fn($($A1),+) -> R1> for fn($($A2),+) -> R2
393        where
394            R2: UpcastFrom<R1>,
395            $($A1: UpcastFrom<$A2>,)+
396        {}
397
398        impl<'a, R1, R2, $($A1, $A2),+> UpcastFrom<dyn Fn($($A1),+) -> R1 + 'a> for dyn Fn($($A2),+) -> R2 + 'a
399        where
400            R2: UpcastFrom<R1>,
401            $($A1: UpcastFrom<$A2>,)+
402        {}
403
404        impl<'a, R1, R2, $($A1, $A2),+> UpcastFrom<dyn FnMut($($A1),+) -> R1 + 'a> for dyn FnMut($($A2),+) -> R2 + 'a
405        where
406            R2: UpcastFrom<R1>,
407            $($A1: UpcastFrom<$A2>,)+
408        {}
409    };
410
411    // Cross-all: done
412    (@cross_all $args:tt $opts:tt) => {};
413
414    // Cross-all: process next
415    (@cross_all $args:tt [$($opts:ident)*] [$next_n:tt $next_args:tt $next_opt:ident] $([$rest_n:tt $rest_args:tt $($rest_opt:ident)?])*) => {
416        impl_fn_upcasts!(@extend $args [$($opts)* $next_opt]);
417        impl_fn_upcasts!(@shrink $args [$($opts)* $next_opt]);
418        impl_fn_upcasts!(@cross_all $args [$($opts)* $next_opt] $([$rest_n $rest_args $($rest_opt)?])*);
419    };
420
421    // Extend: 0 -> N
422    (@extend [] [$($O:ident)+]) => {
423        impl<R1, R2, $($O),+> UpcastFrom<fn() -> R1> for fn($($O),+) -> R2
424        where
425            R2: UpcastFrom<R1>,
426            $($O: UpcastFrom<Undefined>,)+
427        {}
428
429        impl<'a, R1, R2, $($O),+> UpcastFrom<dyn Fn() -> R1 + 'a> for dyn Fn($($O),+) -> R2 + 'a
430        where
431            R2: UpcastFrom<R1>,
432            $($O: UpcastFrom<Undefined>,)+
433        {}
434
435        impl<'a, R1, R2, $($O),+> UpcastFrom<dyn FnMut() -> R1 + 'a> for dyn FnMut($($O),+) -> R2 + 'a
436        where
437            R2: UpcastFrom<R1>,
438            $($O: UpcastFrom<Undefined>,)+
439        {}
440    };
441
442    // Extend: N -> M
443    (@extend [$($A1:ident $A2:ident)+] [$($O:ident)+]) => {
444        impl<R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<fn($($A1),+) -> R1> for fn($($A2,)+ $($O),+) -> R2
445        where
446            R2: UpcastFrom<R1>,
447            $($A1: UpcastFrom<$A2>,)+  // Contravariant
448            $($O: UpcastFrom<Undefined>,)+
449        {}
450
451        impl<'a, R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<dyn Fn($($A1),+) -> R1 + 'a> for dyn Fn($($A2,)+ $($O),+) -> R2 + 'a
452        where
453            R2: UpcastFrom<R1>,
454            $($A1: UpcastFrom<$A2>,)+  // Contravariant
455            $($O: UpcastFrom<Undefined>,)+
456        {}
457
458        impl<'a, R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<dyn FnMut($($A1),+) -> R1 + 'a> for dyn FnMut($($A2,)+ $($O),+) -> R2 + 'a
459        where
460            R2: UpcastFrom<R1>,
461            $($A1: UpcastFrom<$A2>,)+  // Contravariant
462            $($O: UpcastFrom<Undefined>,)+
463        {}
464    };
465
466    // Shrink: N -> 0
467    (@shrink [] [$($O:ident)+]) => {
468        impl<R1, R2, $($O),+> UpcastFrom<fn($($O),+) -> R1> for fn() -> R2
469        where
470            R2: UpcastFrom<R1>,
471            $($O: UpcastFrom<Undefined>,)+
472        {}
473
474        impl<'a, R1, R2, $($O),+> UpcastFrom<dyn Fn($($O),+) -> R1 + 'a> for dyn Fn() -> R2 + 'a
475        where
476            R2: UpcastFrom<R1>,
477            $($O: UpcastFrom<Undefined>,)+
478        {}
479
480        impl<'a, R1, R2, $($O),+> UpcastFrom<dyn FnMut($($O),+) -> R1 + 'a> for dyn FnMut() -> R2 + 'a
481        where
482            R2: UpcastFrom<R1>,
483            $($O: UpcastFrom<Undefined>,)+
484        {}
485    };
486
487    // Shrink: M -> N
488    (@shrink [$($A1:ident $A2:ident)+] [$($O:ident)+]) => {
489        impl<R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<fn($($A1,)+ $($O),+) -> R1> for fn($($A2),+) -> R2
490        where
491            R2: UpcastFrom<R1>,
492            $($A1: UpcastFrom<$A2>,)+  // Contravariant
493            $($O: UpcastFrom<Undefined>,)+
494        {}
495
496        impl<'a, R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<dyn Fn($($A1,)+ $($O),+) -> R1 + 'a> for dyn Fn($($A2),+) -> R2 + 'a
497        where
498            R2: UpcastFrom<R1>,
499            $($A1: UpcastFrom<$A2>,)+  // Contravariant
500            $($O: UpcastFrom<Undefined>,)+
501        {}
502
503        impl<'a, R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<dyn FnMut($($A1,)+ $($O),+) -> R1 + 'a> for dyn FnMut($($A2),+) -> R2 + 'a
504        where
505            R2: UpcastFrom<R1>,
506            $($A1: UpcastFrom<$A2>,)+  // Contravariant
507            $($O: UpcastFrom<Undefined>,)+
508        {}
509    };
510}
511
512impl_fn_upcasts!();
513
514// Copy the above impls down here for where there's only one argument and it's a
515// reference. We could add more impls for more kinds of references, but it
516// becomes a combinatorial explosion quickly. Let's see how far we can get with
517// just this one! Maybe someone else can figure out voodoo so we don't have to
518// duplicate.
519
520// We need to allow coherence leak check just for these traits because we're providing separate implementation for `Fn(&A)` variants when `Fn(A)` one already exists.
521#[allow(coherence_leak_check)]
522const _: () = {
523    #[cfg(all(feature = "std", target_arch = "wasm32", panic = "unwind"))]
524    closures!(@impl_for_args (&A) RefFromWasmAbi [T: core::panic::UnwindSafe,] try_ref_from_abi(&*A) => A a1 a2 a3 a4);
525
526    #[cfg(not(all(feature = "std", target_arch = "wasm32", panic = "unwind")))]
527    closures!(@impl_for_args (&A) RefFromWasmAbi [] try_ref_from_abi(&*A) => A a1 a2 a3 a4);
528};
529
530// UpcastFrom impl for ScopedClosure.
531// ScopedClosure<T1> upcasts to ScopedClosure<T2> when the underlying closure type T1 upcasts to T2.
532// The dyn Fn/FnMut UpcastFrom impls above encode correct variance (covariant return, contravariant args).
533//
534// The 'a: 'b bound encodes the covariant lifetime relationship. Lifetime safety
535// no longer rests on this bound alone: `ScopedClosure<'a, _>::Repr` now carries
536// `'a`, so the `Repr`-equality gate on the upcast itself rejects any lifetime
537// laundering independently (#5176).
538impl<'a: 'b, 'b, T1, T2> UpcastFrom<ScopedClosure<'a, T1>> for ScopedClosure<'b, T2>
539where
540    T1: ?Sized + WasmClosure,
541    T2: ?Sized + WasmClosure + UpcastFrom<T1>,
542{
543}