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