Mock function

I wanted to implement the mock function by replacing instructions, and I did. However, in actual use, it is found that in version 1.60.0, double release occurs occasionally. From the MIR, I see a change in the asynchronous implementation of RUST,

Original function:

   async fn mock_instance_fun(this: &TestStruct, data: String) -> String {}

MIR:

rustc: 1.60.0
  fn mock_instance_fun(_1: &TestStruct, _2: String) -> impl Future<Output = [async output]> {
  fn mock_instance_fun::{closure#0}(_1: Pin<&mut [static generator@src/main.rs:44:68: 49:2]>, _2: ResumeTy) -> GeneratorState<(), String> {

rustc:  1.68.0
  fn mock_instance_fun(_1: &TestStruct, _2: String) -> impl Future<Output = String> {
  fn mock_instance_fun::{closure#0}(_1: Pin<&mut [async fn body@src/main.rs:44:68: 49:2]>, _2: &mut Context<'_>) -> Poll<String> {

Will there be any changes to the async fn in the community? Or someone can tell me where the issue or RFC that tracks this feature is.

I'm not sure what "asynchronous mechanism" you're referring to, but what async fn desugars to is strictly an implementation detail. That fact is unlikely to ever change, in my opinion.

Theoretically, it won't change much, but I didn't find a corresponding issue or RFC track. After testing, I found that the change from GeneratorState to Poll merged into 1.67.1, but I didn't find MR for this either.

rustc: 1.67.0
  fn mock_instance_fun(_1: &TestStruct, _2: String) -> impl Future<Output = String> {
  fn mock_instance_fun::{closure#0}(_1: Pin<&mut [static generator@src/main.rs:44:68: 49:2]>, _2: ResumeTy) -> GeneratorState<(), String> {

rustc:  1.67.1
  fn mock_instance_fun(_1: &TestStruct, _2: String) -> impl Future<Output = String> {
  fn mock_instance_fun::{closure#0}(_1: Pin<&mut [async fn body@src/main.rs:44:68: 49:2]>, _2: &mut Context<'_>) -> Poll<String> {

Precisely because it's an implementation detail.

2 Likes

You're right. There won't be a big change in this area. I found the code for the change.

@Swatinem Hello, may I ask you, is there any plan to change this section in the future? Thank you very much.

You won't get any promise on this area. It's an implementation detail and will change anytime without an announcement.

1 Like

@silence-coding I just landed Remove `identity_future` indirection by Swatinem · Pull Request #104833 · rust-lang/rust · GitHub recently which is related to async lowering. WIP: Remove `ResumeTy` from async lowering by Swatinem · Pull Request #107562 · rust-lang/rust · GitHub is blocked on other compiler internal work, so it won’t be landing anytime soon.

But as you are talking about MIR, none of those two should have an impact on that.

[WIP] mir-opt for generators/futures: copy propagate upvar into locals by pnkfelix · Pull Request #108590 · rust-lang/rust · GitHub is also being worked on which I am super excited about, which might completely inline child futures, though I believe it does not change the signature of those functions, but rather the data types backing the future.

Also, I want to repeat what @zirconium-n said: These are implementation details that have no stability guarantees whatsoever (unless I am complete mistaken?), so these things can and will change between releases without notice.

3 Likes

Thanks

Thank you. I understand.

I have a problem, replacing the async fn with jump instructions(like https://github.com/bouk/monkey) works fine on x86, but not on arm.

I will replace generator and poll of instance_method with mock_instance_fun.

  fn mock_instance_fun(_1: &TestStruct, _2: String) -> impl Future<Output = String> {
  fn mock_instance_fun::{closure#0}(_1: Pin<&mut [async fn body@src/main.rs:44:68: 49:2]>, _2: &mut Context<'_>) -> Poll<String> {
#[tokio::main]
async fn main() {
    let ins = TestStruct {};
    println!("{}", ins.instance_method(String::from("actual")).await);
    let _mock = mock::stub_async_func!(TestStruct::instance_method, mock_instance_fun);
    println!("{}", ins.instance_method(String::from("actual")).await);
}

struct TestStruct {}
impl TestStruct {
    pub async fn instance_method(&self, data: String) -> String {
        let data = self.instance_method1(data).await;  // If instance_method1 is commented out, it can also be run in ARM.
        self.instance_method2(data).await
    }
    pub async fn instance_method1(&self, data: String) -> String {
        data
    }
    pub async fn instance_method2(&self, data: String) -> String {
        data
    }
}

async fn mock_instance_fun(_: &TestStruct, _data: String) -> String {
    "MOCK".to_owned()
}

Segmentation fault:

Thread 1 "client" received signal SIGSEGV, Segmentation fault.
0x0000ffffbf52c194 in free () from /lib64/libc.so.6
Missing separate debuginfos, use: dnf debuginfo-install libgcc-7.3.0-20190804.h22.eulerosv2r8.aarch64
(gdb) bt
#0  0x0000ffffbf52c194 in free () from /lib64/libc.so.6
#1  0x0000aaaaaab55674 in alloc::alloc::dealloc () at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/alloc/src/alloc.rs:113
#2  <alloc::alloc::Global as core::alloc::Allocator>::deallocate (self=0xffffffffe710, ptr=..., layout=...)
    at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/alloc/src/alloc.rs:250
#3  0x0000aaaaaab55d50 in <alloc::raw_vec::RawVec<T,A> as core::ops::drop::Drop>::drop (self=0xffffffffe710)
    at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/alloc/src/raw_vec.rs:479
#4  0x0000aaaaaab54e58 in core::ptr::drop_in_place<alloc::raw_vec::RawVec<u8>> () at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/ptr/mod.rs:490
#5  0x0000aaaaaab54de4 in core::ptr::drop_in_place<alloc::vec::Vec<u8>> () at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/ptr/mod.rs:490
#6  0x0000aaaaaab54d98 in core::ptr::drop_in_place<alloc::string::String> () at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/ptr/mod.rs:490
#7  0x0000aaaaaaabc028 in core::ptr::drop_in_place<client::TestStruct::instance_method::{{closure}}> ()
    at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/ptr/mod.rs:20
#8  0x0000aaaaaaabb654 in client::main::{{closure}} () at src/main.rs:15
#9  0x0000aaaaaaabdb3c in tokio::park::thread::CachedParkThread::block_on::{{closure}} () at /tokio-1.20.1/src/park/thread.rs:263
#10 0x0000aaaaaaabcf68 in tokio::coop::with_budget::{{closure}} (cell=0xffffbf6ea78a) at /tokio-1.20.1/src/coop.rs:102
#11 0x0000aaaaaaabcb5c in std::thread::local::LocalKey<T>::try_with (self=0xaaaaaac11b10, f=...)
    at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/thread/local.rs:446
#12 0x0000aaaaaaabc604 in std::thread::local::LocalKey<T>::with (self=0xaaaaaac11b10, f=...)
    at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/thread/local.rs:422
#13 0x0000aaaaaaabd9a8 in tokio::coop::with_budget (budget=..., f=...) at /tokio-1.20.1/src/coop.rs:95
#14 tokio::coop::budget (f=...) at /tokio-1.20.1/src/coop.rs:72
#15 tokio::park::thread::CachedParkThread::block_on (self=0xffffffffe87f, f=...) at /tokio-1.20.1/src/park/thread.rs:263
#16 0x0000aaaaaaabc528 in tokio::runtime::enter::Enter::block_on (self=0xffffffffe96f, f=...) at /tokio-1.20.1/src/runtime/enter.rs:152
#17 0x0000aaaaaaaba994 in tokio::runtime::thread_pool::ThreadPool::block_on (self=0xffffffffed00, future=...) at /tokio-1.20.1/src/runtime/thread_pool/mod.rs:90
#18 0x0000aaaaaaabbce4 in tokio::runtime::Runtime::block_on (self=0xffffffffed00, future=...) at /tokio-1.20.1/src/runtime/mod.rs:484
#19 0x0000aaaaaaab8fd4 in client::main () at src/main.rs:15

```

fn <impl at src/main.rs:19:1: 19:16>::instance_method(_1: &TestStruct, _2: String) -> impl Future<Output = String> {
    debug self => _1;                    // in scope 0 at src/main.rs:20:34: 20:39
    debug data => _2;                    // in scope 0 at src/main.rs:20:41: 20:45
    let mut _0: impl std::future::Future<Output = std::string::String>; // return place in scope 0 at src/main.rs:20:58: 20:64
    let mut _3: [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6

    bb0: {
        Deinit(_3);                      // scope 0 at src/main.rs:20:65: 23:6
        (_3.0: &TestStruct) = _1;        // scope 0 at src/main.rs:20:65: 23:6
        (_3.1: std::string::String) = move _2; // scope 0 at src/main.rs:20:65: 23:6
        discriminant(_3) = 0;            // scope 0 at src/main.rs:20:65: 23:6
        _0 = identity_future::<String, [async fn body@src/main.rs:20:65: 23:6]>(move _3) -> bb1; // scope 0 at src/main.rs:20:65: 23:6
                                         // mir::Constant
                                         // + span: src/main.rs:20:65: 23:6
                                         // + literal: Const { ty: fn([async fn body@src/main.rs:20:65: 23:6]) -> [async fn body@src/main.rs:20:65: 23:6] {identity_future::<String, [async fn body@src/main.rs:20:65: 23:6]>}, val: Value(<ZST>) }
    }

    bb1: {
        return;                          // scope 0 at src/main.rs:23:6: 23:6
    }
}

fn <impl at src/main.rs:19:1: 19:16>::instance_method::{closure#0}(_1: Pin<&mut [async fn body@src/main.rs:20:65: 23:6]>, _2: &mut Context<'_>) -> Poll<String> {
    debug _task_context => _34;          // in scope 0 at src/main.rs:20:65: 23:6
    debug self => ((*(_1.0: &mut [async fn body@src/main.rs:20:65: 23:6])).0: &TestStruct); // in scope 0 at src/main.rs:20:34: 20:39
    debug data => ((*(_1.0: &mut [async fn body@src/main.rs:20:65: 23:6])).1: std::string::String); // in scope 0 at src/main.rs:20:41: 20:45
    let mut _0: std::task::Poll<std::string::String>; // return place in scope 0 at src/main.rs:20:65: 23:6
    let mut _5: impl std::future::Future<Output = std::string::String>; // in scope 0 at src/main.rs:21:47: 21:53
    let mut _6: impl std::future::Future<Output = std::string::String>; // in scope 0 at src/main.rs:21:20: 21:47
    let mut _7: &TestStruct;             // in scope 0 at src/main.rs:21:20: 21:47
    let mut _8: std::string::String;     // in scope 0 at src/main.rs:21:42: 21:46
    let mut _9: std::task::Poll<std::string::String>; // in scope 0 at src/main.rs:21:47: 21:53
    let mut _10: std::pin::Pin<&mut impl std::future::Future<Output = std::string::String>>; // in scope 0 at src/main.rs:21:47: 21:53
    let mut _11: &mut impl std::future::Future<Output = std::string::String>; // in scope 0 at src/main.rs:21:47: 21:53
    let mut _12: &mut impl std::future::Future<Output = std::string::String>; // in scope 0 at src/main.rs:21:47: 21:53
    let mut _13: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:21:20: 21:53
    let mut _14: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:21:20: 21:53
    let mut _15: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:21:47: 21:53
    let mut _16: isize;                  // in scope 0 at src/main.rs:21:47: 21:53
    let mut _18: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:21:47: 21:53
    let mut _19: impl std::future::Future<Output = std::string::String>; // in scope 0 at src/main.rs:22:36: 22:42
    let mut _20: impl std::future::Future<Output = std::string::String>; // in scope 0 at src/main.rs:22:9: 22:36
    let mut _21: &TestStruct;            // in scope 0 at src/main.rs:22:9: 22:36
    let mut _22: std::string::String;    // in scope 0 at src/main.rs:22:31: 22:35
    let mut _23: std::task::Poll<std::string::String>; // in scope 0 at src/main.rs:22:36: 22:42
    let mut _24: std::pin::Pin<&mut impl std::future::Future<Output = std::string::String>>; // in scope 0 at src/main.rs:22:36: 22:42
    let mut _25: &mut impl std::future::Future<Output = std::string::String>; // in scope 0 at src/main.rs:22:36: 22:42
    let mut _26: &mut impl std::future::Future<Output = std::string::String>; // in scope 0 at src/main.rs:22:36: 22:42
    let mut _27: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:22:9: 22:42
    let mut _28: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:22:9: 22:42
    let mut _29: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:22:36: 22:42
    let mut _30: isize;                  // in scope 0 at src/main.rs:22:36: 22:42
    let mut _32: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:22:36: 22:42
    let mut _33: std::string::String;    // in scope 0 at src/main.rs:20:65: 23:6
    let mut _34: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _35: u32;                    // in scope 0 at src/main.rs:20:65: 23:6
    let mut _36: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _37: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _38: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _39: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _40: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _41: &TestStruct;            // in scope 0 at src/main.rs:20:65: 23:6
    let mut _42: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _43: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _44: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _45: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _46: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _47: &TestStruct;            // in scope 0 at src/main.rs:20:65: 23:6
    let mut _48: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _49: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _50: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _51: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _52: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _53: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _54: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    let mut _55: &mut [async fn body@src/main.rs:20:65: 23:6]; // in scope 0 at src/main.rs:20:65: 23:6
    scope 1 {
        debug self => (((*(_1.0: &mut [async fn body@src/main.rs:20:65: 23:6])) as variant#3).0: &TestStruct); // in scope 1 at src/main.rs:20:34: 20:39
        let _3: std::string::String;     // in scope 1 at src/main.rs:20:41: 20:45
        scope 2 {
            debug data => _3;            // in scope 2 at src/main.rs:20:41: 20:45
            let _4: std::string::String; // in scope 2 at src/main.rs:21:13: 21:17
            scope 3 {
                debug data => _4;        // in scope 3 at src/main.rs:21:13: 21:17
                scope 7 {
                    debug __awaitee => (((*(_1.0: &mut [async fn body@src/main.rs:20:65: 23:6])) as variant#4).0: impl std::future::Future<Output = std::string::String>); // in scope 7 at src/main.rs:22:36: 22:42
                    let _31: std::string::String; // in scope 7 at src/main.rs:22:9: 22:42
                    scope 8 {
                    }
                    scope 9 {
                        debug result => _31; // in scope 9 at src/main.rs:22:9: 22:42
                    }
                }
            }
            scope 4 {
                debug __awaitee => (((*(_1.0: &mut [async fn body@src/main.rs:20:65: 23:6])) as variant#3).1: impl std::future::Future<Output = std::string::String>); // in scope 4 at src/main.rs:21:47: 21:53
                let _17: std::string::String; // in scope 4 at src/main.rs:21:20: 21:53
                scope 5 {
                }
                scope 6 {
                    debug result => _17; // in scope 6 at src/main.rs:21:20: 21:53
                }
            }
        }
    }

    bb0: {
        _36 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 0 at src/main.rs:20:65: 23:6
        _35 = discriminant((*_36));      // scope 0 at src/main.rs:20:65: 23:6
        switchInt(move _35) -> [0: bb1, 1: bb26, 2: bb25, 3: bb23, 4: bb24, otherwise: bb27]; // scope 0 at src/main.rs:20:65: 23:6
    }

    bb1: {
        _34 = move _2;                   // scope 0 at src/main.rs:20:65: 23:6
        _37 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 0 at src/main.rs:20:34: 20:39
        _38 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 0 at src/main.rs:20:34: 20:39
        (((*_37) as variant#3).0: &TestStruct) = ((*_38).0: &TestStruct); // scope 0 at src/main.rs:20:34: 20:39
        _39 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 1 at src/main.rs:20:41: 20:45
        _3 = move ((*_39).1: std::string::String); // scope 1 at src/main.rs:20:41: 20:45
        _40 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 2 at src/main.rs:21:20: 21:47
        _41 = deref_copy (((*_40) as variant#3).0: &TestStruct); // scope 2 at src/main.rs:21:20: 21:47
        _7 = _41;                        // scope 2 at src/main.rs:21:20: 21:47
        _8 = move _3;                    // scope 2 at src/main.rs:21:42: 21:46
        _6 = TestStruct::instance_method1(move _7, move _8) -> [return: bb2, unwind: bb22]; // scope 2 at src/main.rs:21:20: 21:47
                                         // mir::Constant
                                         // + span: src/main.rs:21:25: 21:41
                                         // + literal: Const { ty: for<'a> fn(&'a TestStruct, String) -> impl Future<Output = String> {TestStruct::instance_method1}, val: Value(<ZST>) }
    }

    bb2: {
        _5 = <impl Future<Output = String> as IntoFuture>::into_future(move _6) -> [return: bb3, unwind: bb22]; // scope 2 at src/main.rs:21:47: 21:53
                                         // mir::Constant
                                         // + span: src/main.rs:21:47: 21:53
                                         // + literal: Const { ty: fn(impl Future<Output = String>) -> <impl Future<Output = String> as IntoFuture>::IntoFuture {<impl Future<Output = String> as IntoFuture>::into_future}, val: Value(<ZST>) }
    }

    bb3: {
        _42 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 2 at src/main.rs:21:47: 21:53
        (((*_42) as variant#3).1: impl std::future::Future<Output = std::string::String>) = move _5; // scope 2 at src/main.rs:21:47: 21:53
        goto -> bb4;                     // scope 4 at src/main.rs:21:47: 21:53
    }

    bb4: {
        _43 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 5 at src/main.rs:21:47: 21:53
        _12 = &mut (((*_43) as variant#3).1: impl std::future::Future<Output = std::string::String>); // scope 5 at src/main.rs:21:47: 21:53
        _11 = &mut (*_12);               // scope 5 at src/main.rs:21:47: 21:53
        _10 = Pin::<&mut impl Future<Output = String>>::new_unchecked(move _11) -> [return: bb5, unwind: bb21]; // scope 5 at src/main.rs:21:47: 21:53
                                         // mir::Constant
                                         // + span: src/main.rs:21:47: 21:53
                                         // + literal: Const { ty: unsafe fn(&mut impl Future<Output = String>) -> Pin<&mut impl Future<Output = String>> {Pin::<&mut impl Future<Output = String>>::new_unchecked}, val: Value(<ZST>) }
    }

    bb5: {
        _15 = _34;                       // scope 5 at src/main.rs:21:47: 21:53
        _14 = move _15;                  // scope 5 at src/main.rs:21:20: 21:53
        _13 = &mut (*_14);               // scope 5 at src/main.rs:21:20: 21:53
        _9 = <impl Future<Output = String> as Future>::poll(move _10, move _13) -> [return: bb6, unwind: bb21]; // scope 5 at src/main.rs:21:47: 21:53
                                         // mir::Constant
                                         // + span: src/main.rs:21:47: 21:53
                                         // + literal: Const { ty: for<'a, 'b, 'c> fn(Pin<&'a mut impl Future<Output = String>>, &'b mut Context<'c>) -> Poll<<impl Future<Output = String> as Future>::Output> {<impl Future<Output = String> as Future>::poll}, val: Value(<ZST>) }
    }

    bb6: {
        _16 = discriminant(_9);          // scope 4 at src/main.rs:21:47: 21:53
        switchInt(move _16) -> [0: bb9, 1: bb7, otherwise: bb8]; // scope 4 at src/main.rs:21:47: 21:53
    }

    bb7: {
        Deinit(_0);                      // scope 4 at src/main.rs:21:47: 21:53
        discriminant(_0) = 1;            // scope 4 at src/main.rs:21:47: 21:53
        _44 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 4 at src/main.rs:21:47: 21:53
        discriminant((*_44)) = 3;        // scope 4 at src/main.rs:21:47: 21:53
        return;                          // scope 4 at src/main.rs:21:47: 21:53
    }

    bb8: {
        unreachable;                     // scope 4 at src/main.rs:21:47: 21:53
    }

    bb9: {
        _17 = move ((_9 as Ready).0: std::string::String); // scope 4 at src/main.rs:21:20: 21:53
        _4 = move _17;                   // scope 6 at src/main.rs:21:20: 21:53
        _45 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 2 at src/main.rs:21:52: 21:53
        drop((((*_45) as variant#3).1: impl std::future::Future<Output = std::string::String>)) -> [return: bb10, unwind: bb22]; // scope 2 at src/main.rs:21:52: 21:53
    }

    bb10: {
        _46 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 3 at src/main.rs:22:9: 22:36
        _47 = deref_copy (((*_46) as variant#3).0: &TestStruct); // scope 3 at src/main.rs:22:9: 22:36
        _21 = _47;                       // scope 3 at src/main.rs:22:9: 22:36
        _22 = move _4;                   // scope 3 at src/main.rs:22:31: 22:35
        _20 = TestStruct::instance_method2(move _21, move _22) -> [return: bb11, unwind: bb22]; // scope 3 at src/main.rs:22:9: 22:36
                                         // mir::Constant
                                         // + span: src/main.rs:22:14: 22:30
                                         // + literal: Const { ty: for<'a> fn(&'a TestStruct, String) -> impl Future<Output = String> {TestStruct::instance_method2}, val: Value(<ZST>) }
    }

    bb11: {
        _19 = <impl Future<Output = String> as IntoFuture>::into_future(move _20) -> [return: bb12, unwind: bb22]; // scope 3 at src/main.rs:22:36: 22:42
                                         // mir::Constant
                                         // + span: src/main.rs:22:36: 22:42
                                         // + literal: Const { ty: fn(impl Future<Output = String>) -> <impl Future<Output = String> as IntoFuture>::IntoFuture {<impl Future<Output = String> as IntoFuture>::into_future}, val: Value(<ZST>) }
    }

    bb12: {
        _48 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 3 at src/main.rs:22:36: 22:42
        (((*_48) as variant#4).0: impl std::future::Future<Output = std::string::String>) = move _19; // scope 3 at src/main.rs:22:36: 22:42
        goto -> bb13;                    // scope 7 at src/main.rs:22:36: 22:42
    }

    bb13: {
        _49 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 8 at src/main.rs:22:36: 22:42
        _26 = &mut (((*_49) as variant#4).0: impl std::future::Future<Output = std::string::String>); // scope 8 at src/main.rs:22:36: 22:42
        _25 = &mut (*_26);               // scope 8 at src/main.rs:22:36: 22:42
        _24 = Pin::<&mut impl Future<Output = String>>::new_unchecked(move _25) -> [return: bb14, unwind: bb20]; // scope 8 at src/main.rs:22:36: 22:42
                                         // mir::Constant
                                         // + span: src/main.rs:22:36: 22:42
                                         // + literal: Const { ty: unsafe fn(&mut impl Future<Output = String>) -> Pin<&mut impl Future<Output = String>> {Pin::<&mut impl Future<Output = String>>::new_unchecked}, val: Value(<ZST>) }
    }

    bb14: {
        _29 = _34;                       // scope 8 at src/main.rs:22:36: 22:42
        _28 = move _29;                  // scope 8 at src/main.rs:22:9: 22:42
        _27 = &mut (*_28);               // scope 8 at src/main.rs:22:9: 22:42
        _23 = <impl Future<Output = String> as Future>::poll(move _24, move _27) -> [return: bb15, unwind: bb20]; // scope 8 at src/main.rs:22:36: 22:42
                                         // mir::Constant
                                         // + span: src/main.rs:22:36: 22:42
                                         // + literal: Const { ty: for<'a, 'b, 'c> fn(Pin<&'a mut impl Future<Output = String>>, &'b mut Context<'c>) -> Poll<<impl Future<Output = String> as Future>::Output> {<impl Future<Output = String> as Future>::poll}, val: Value(<ZST>) }
    }

    bb15: {
        _30 = discriminant(_23);         // scope 7 at src/main.rs:22:36: 22:42
        switchInt(move _30) -> [0: bb18, 1: bb16, otherwise: bb17]; // scope 7 at src/main.rs:22:36: 22:42
    }

    bb16: {
        Deinit(_0);                      // scope 7 at src/main.rs:22:36: 22:42
        discriminant(_0) = 1;            // scope 7 at src/main.rs:22:36: 22:42
        _50 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 7 at src/main.rs:22:36: 22:42
        discriminant((*_50)) = 4;        // scope 7 at src/main.rs:22:36: 22:42
        return;                          // scope 7 at src/main.rs:22:36: 22:42
    }

    bb17: {
        unreachable;                     // scope 7 at src/main.rs:22:36: 22:42
    }

    bb18: {
        _31 = move ((_23 as Ready).0: std::string::String); // scope 7 at src/main.rs:22:9: 22:42
        _33 = move _31;                  // scope 9 at src/main.rs:22:9: 22:42
        _51 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 3 at src/main.rs:22:41: 22:42
        drop((((*_51) as variant#4).0: impl std::future::Future<Output = std::string::String>)) -> [return: bb19, unwind: bb22]; // scope 3 at src/main.rs:22:41: 22:42
    }

    bb19: {
        Deinit(_0);                      // scope 0 at src/main.rs:23:6: 23:6
        ((_0 as Ready).0: std::string::String) = move _33; // scope 0 at src/main.rs:23:6: 23:6
        discriminant(_0) = 0;            // scope 0 at src/main.rs:23:6: 23:6
        _52 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 0 at src/main.rs:23:6: 23:6
        discriminant((*_52)) = 1;        // scope 0 at src/main.rs:23:6: 23:6
        return;                          // scope 0 at src/main.rs:23:6: 23:6
    }

    bb20 (cleanup): {
        _53 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 3 at src/main.rs:22:41: 22:42
        drop((((*_53) as variant#4).0: impl std::future::Future<Output = std::string::String>)) -> bb22; // scope 3 at src/main.rs:22:41: 22:42
    }

    bb21 (cleanup): {
        _54 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 2 at src/main.rs:21:52: 21:53
        drop((((*_54) as variant#3).1: impl std::future::Future<Output = std::string::String>)) -> bb22; // scope 2 at src/main.rs:21:52: 21:53
    }

    bb22 (cleanup): {
        _55 = deref_copy (_1.0: &mut [async fn body@src/main.rs:20:65: 23:6]); // scope 0 at src/main.rs:20:65: 23:6
        discriminant((*_55)) = 2;        // scope 0 at src/main.rs:20:65: 23:6
        resume;                          // scope 0 at src/main.rs:20:65: 23:6
    }

    bb23: {
        _18 = move _2;                   // scope 0 at src/main.rs:20:65: 23:6
        _34 = move _18;                  // scope 4 at src/main.rs:21:47: 21:53
        goto -> bb4;                     // scope 4 at src/main.rs:21:47: 21:53
    }

    bb24: {
        _32 = move _2;                   // scope 0 at src/main.rs:20:65: 23:6
        _34 = move _32;                  // scope 7 at src/main.rs:22:36: 22:42
        goto -> bb13;                    // scope 7 at src/main.rs:22:36: 22:42
    }

    bb25: {
        assert(const false, "`async fn` resumed after panicking") -> bb25; // scope 0 at src/main.rs:20:65: 23:6
    }

    bb26: {
        assert(const false, "`async fn` resumed after completion") -> bb26; // scope 0 at src/main.rs:20:65: 23:6
    }

    bb27: {
        unreachable;                     // scope 0 at src/main.rs:20:65: 23:6
    }
}

fn <impl at src/main.rs:19:1: 19:16>::instance_method1(_1: &TestStruct, _2: String) -> impl Future<Output = String> {
    debug self => _1;                    // in scope 0 at src/main.rs:24:35: 24:40
    debug data => _2;                    // in scope 0 at src/main.rs:24:42: 24:46
    let mut _0: impl std::future::Future<Output = std::string::String>; // return place in scope 0 at src/main.rs:24:59: 24:65
    let mut _3: [async fn body@src/main.rs:24:66: 26:6]; // in scope 0 at src/main.rs:24:66: 26:6

    bb0: {
        Deinit(_3);                      // scope 0 at src/main.rs:24:66: 26:6
        (_3.0: &TestStruct) = _1;        // scope 0 at src/main.rs:24:66: 26:6
        (_3.1: std::string::String) = move _2; // scope 0 at src/main.rs:24:66: 26:6
        discriminant(_3) = 0;            // scope 0 at src/main.rs:24:66: 26:6
        _0 = identity_future::<String, [async fn body@src/main.rs:24:66: 26:6]>(move _3) -> bb1; // scope 0 at src/main.rs:24:66: 26:6
                                         // mir::Constant
                                         // + span: src/main.rs:24:66: 26:6
                                         // + literal: Const { ty: fn([async fn body@src/main.rs:24:66: 26:6]) -> [async fn body@src/main.rs:24:66: 26:6] {identity_future::<String, [async fn body@src/main.rs:24:66: 26:6]>}, val: Value(<ZST>) }
    }

    bb1: {
        return;                          // scope 0 at src/main.rs:26:6: 26:6
    }
}

fn <impl at src/main.rs:19:1: 19:16>::instance_method1::{closure#0}(_1: Pin<&mut [async fn body@src/main.rs:24:66: 26:6]>, _2: &mut Context<'_>) -> Poll<String> {
    debug _task_context => _6;           // in scope 0 at src/main.rs:24:66: 26:6
    debug self => ((*(_1.0: &mut [async fn body@src/main.rs:24:66: 26:6])).0: &TestStruct); // in scope 0 at src/main.rs:24:35: 24:40
    debug data => ((*(_1.0: &mut [async fn body@src/main.rs:24:66: 26:6])).1: std::string::String); // in scope 0 at src/main.rs:24:42: 24:46
    let mut _0: std::task::Poll<std::string::String>; // return place in scope 0 at src/main.rs:24:66: 26:6
    let _3: &TestStruct;                 // in scope 0 at src/main.rs:24:35: 24:40
    let mut _5: std::string::String;     // in scope 0 at src/main.rs:24:66: 26:6
    let mut _6: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:24:66: 26:6
    let mut _7: u32;                     // in scope 0 at src/main.rs:24:66: 26:6
    let mut _8: &mut [async fn body@src/main.rs:24:66: 26:6]; // in scope 0 at src/main.rs:24:66: 26:6
    let mut _9: &mut [async fn body@src/main.rs:24:66: 26:6]; // in scope 0 at src/main.rs:24:66: 26:6
    let mut _10: &mut [async fn body@src/main.rs:24:66: 26:6]; // in scope 0 at src/main.rs:24:66: 26:6
    let mut _11: &mut [async fn body@src/main.rs:24:66: 26:6]; // in scope 0 at src/main.rs:24:66: 26:6
    scope 1 {
        debug self => _3;                // in scope 1 at src/main.rs:24:35: 24:40
        let _4: std::string::String;     // in scope 1 at src/main.rs:24:42: 24:46
        scope 2 {
            debug data => _4;            // in scope 2 at src/main.rs:24:42: 24:46
        }
    }

    bb0: {
        _8 = deref_copy (_1.0: &mut [async fn body@src/main.rs:24:66: 26:6]); // scope 0 at src/main.rs:24:66: 26:6
        _7 = discriminant((*_8));        // scope 0 at src/main.rs:24:66: 26:6
        switchInt(move _7) -> [0: bb1, 1: bb3, 2: bb2, otherwise: bb4]; // scope 0 at src/main.rs:24:66: 26:6
    }

    bb1: {
        _6 = move _2;                    // scope 0 at src/main.rs:24:66: 26:6
        _9 = deref_copy (_1.0: &mut [async fn body@src/main.rs:24:66: 26:6]); // scope 0 at src/main.rs:24:35: 24:40
        _3 = ((*_9).0: &TestStruct);     // scope 0 at src/main.rs:24:35: 24:40
        _10 = deref_copy (_1.0: &mut [async fn body@src/main.rs:24:66: 26:6]); // scope 1 at src/main.rs:24:42: 24:46
        _4 = move ((*_10).1: std::string::String); // scope 1 at src/main.rs:24:42: 24:46
        _5 = move _4;                    // scope 2 at src/main.rs:25:9: 25:13
        Deinit(_0);                      // scope 0 at src/main.rs:26:6: 26:6
        ((_0 as Ready).0: std::string::String) = move _5; // scope 0 at src/main.rs:26:6: 26:6
        discriminant(_0) = 0;            // scope 0 at src/main.rs:26:6: 26:6
        _11 = deref_copy (_1.0: &mut [async fn body@src/main.rs:24:66: 26:6]); // scope 0 at src/main.rs:26:6: 26:6
        discriminant((*_11)) = 1;        // scope 0 at src/main.rs:26:6: 26:6
        return;                          // scope 0 at src/main.rs:26:6: 26:6
    }

    bb2: {
        assert(const false, "`async fn` resumed after panicking") -> bb2; // scope 0 at src/main.rs:24:66: 26:6
    }

    bb3: {
        assert(const false, "`async fn` resumed after completion") -> bb3; // scope 0 at src/main.rs:24:66: 26:6
    }

    bb4: {
        unreachable;                     // scope 0 at src/main.rs:24:66: 26:6
    }
}

fn <impl at src/main.rs:19:1: 19:16>::instance_method2(_1: &TestStruct, _2: String) -> impl Future<Output = String> {
    debug self => _1;                    // in scope 0 at src/main.rs:27:35: 27:40
    debug data => _2;                    // in scope 0 at src/main.rs:27:42: 27:46
    let mut _0: impl std::future::Future<Output = std::string::String>; // return place in scope 0 at src/main.rs:27:59: 27:65
    let mut _3: [async fn body@src/main.rs:27:66: 29:6]; // in scope 0 at src/main.rs:27:66: 29:6

    bb0: {
        Deinit(_3);                      // scope 0 at src/main.rs:27:66: 29:6
        (_3.0: &TestStruct) = _1;        // scope 0 at src/main.rs:27:66: 29:6
        (_3.1: std::string::String) = move _2; // scope 0 at src/main.rs:27:66: 29:6
        discriminant(_3) = 0;            // scope 0 at src/main.rs:27:66: 29:6
        _0 = identity_future::<String, [async fn body@src/main.rs:27:66: 29:6]>(move _3) -> bb1; // scope 0 at src/main.rs:27:66: 29:6
                                         // mir::Constant
                                         // + span: src/main.rs:27:66: 29:6
                                         // + literal: Const { ty: fn([async fn body@src/main.rs:27:66: 29:6]) -> [async fn body@src/main.rs:27:66: 29:6] {identity_future::<String, [async fn body@src/main.rs:27:66: 29:6]>}, val: Value(<ZST>) }
    }

    bb1: {
        return;                          // scope 0 at src/main.rs:29:6: 29:6
    }
}

fn <impl at src/main.rs:19:1: 19:16>::instance_method2::{closure#0}(_1: Pin<&mut [async fn body@src/main.rs:27:66: 29:6]>, _2: &mut Context<'_>) -> Poll<String> {
    debug _task_context => _6;           // in scope 0 at src/main.rs:27:66: 29:6
    debug self => ((*(_1.0: &mut [async fn body@src/main.rs:27:66: 29:6])).0: &TestStruct); // in scope 0 at src/main.rs:27:35: 27:40
    debug data => ((*(_1.0: &mut [async fn body@src/main.rs:27:66: 29:6])).1: std::string::String); // in scope 0 at src/main.rs:27:42: 27:46
    let mut _0: std::task::Poll<std::string::String>; // return place in scope 0 at src/main.rs:27:66: 29:6
    let _3: &TestStruct;                 // in scope 0 at src/main.rs:27:35: 27:40
    let mut _5: std::string::String;     // in scope 0 at src/main.rs:27:66: 29:6
    let mut _6: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:27:66: 29:6
    let mut _7: u32;                     // in scope 0 at src/main.rs:27:66: 29:6
    let mut _8: &mut [async fn body@src/main.rs:27:66: 29:6]; // in scope 0 at src/main.rs:27:66: 29:6
    let mut _9: &mut [async fn body@src/main.rs:27:66: 29:6]; // in scope 0 at src/main.rs:27:66: 29:6
    let mut _10: &mut [async fn body@src/main.rs:27:66: 29:6]; // in scope 0 at src/main.rs:27:66: 29:6
    let mut _11: &mut [async fn body@src/main.rs:27:66: 29:6]; // in scope 0 at src/main.rs:27:66: 29:6
    scope 1 {
        debug self => _3;                // in scope 1 at src/main.rs:27:35: 27:40
        let _4: std::string::String;     // in scope 1 at src/main.rs:27:42: 27:46
        scope 2 {
            debug data => _4;            // in scope 2 at src/main.rs:27:42: 27:46
        }
    }

    bb0: {
        _8 = deref_copy (_1.0: &mut [async fn body@src/main.rs:27:66: 29:6]); // scope 0 at src/main.rs:27:66: 29:6
        _7 = discriminant((*_8));        // scope 0 at src/main.rs:27:66: 29:6
        switchInt(move _7) -> [0: bb1, 1: bb3, 2: bb2, otherwise: bb4]; // scope 0 at src/main.rs:27:66: 29:6
    }

    bb1: {
        _6 = move _2;                    // scope 0 at src/main.rs:27:66: 29:6
        _9 = deref_copy (_1.0: &mut [async fn body@src/main.rs:27:66: 29:6]); // scope 0 at src/main.rs:27:35: 27:40
        _3 = ((*_9).0: &TestStruct);     // scope 0 at src/main.rs:27:35: 27:40
        _10 = deref_copy (_1.0: &mut [async fn body@src/main.rs:27:66: 29:6]); // scope 1 at src/main.rs:27:42: 27:46
        _4 = move ((*_10).1: std::string::String); // scope 1 at src/main.rs:27:42: 27:46
        _5 = move _4;                    // scope 2 at src/main.rs:28:9: 28:13
        Deinit(_0);                      // scope 0 at src/main.rs:29:6: 29:6
        ((_0 as Ready).0: std::string::String) = move _5; // scope 0 at src/main.rs:29:6: 29:6
        discriminant(_0) = 0;            // scope 0 at src/main.rs:29:6: 29:6
        _11 = deref_copy (_1.0: &mut [async fn body@src/main.rs:27:66: 29:6]); // scope 0 at src/main.rs:29:6: 29:6
        discriminant((*_11)) = 1;        // scope 0 at src/main.rs:29:6: 29:6
        return;                          // scope 0 at src/main.rs:29:6: 29:6
    }

    bb2: {
        assert(const false, "`async fn` resumed after panicking") -> bb2; // scope 0 at src/main.rs:27:66: 29:6
    }

    bb3: {
        assert(const false, "`async fn` resumed after completion") -> bb3; // scope 0 at src/main.rs:27:66: 29:6
    }

    bb4: {
        unreachable;                     // scope 0 at src/main.rs:27:66: 29:6
    }
}

fn mock_instance_fun(_1: &TestStruct, _2: String) -> impl Future<Output = String> {
    debug _ => _1;                       // in scope 0 at src/main.rs:32:28: 32:29
    debug _data => _2;                   // in scope 0 at src/main.rs:32:44: 32:49
    let mut _0: impl std::future::Future<Output = std::string::String>; // return place in scope 0 at src/main.rs:32:62: 32:68
    let mut _3: [async fn body@src/main.rs:32:69: 34:2]; // in scope 0 at src/main.rs:32:69: 34:2

    bb0: {
        Deinit(_3);                      // scope 0 at src/main.rs:32:69: 34:2
        (_3.0: &TestStruct) = _1;        // scope 0 at src/main.rs:32:69: 34:2
        (_3.1: std::string::String) = move _2; // scope 0 at src/main.rs:32:69: 34:2
        discriminant(_3) = 0;            // scope 0 at src/main.rs:32:69: 34:2
        _0 = identity_future::<String, [async fn body@src/main.rs:32:69: 34:2]>(move _3) -> bb1; // scope 0 at src/main.rs:32:69: 34:2
                                         // mir::Constant
                                         // + span: src/main.rs:32:69: 34:2
                                         // + literal: Const { ty: fn([async fn body@src/main.rs:32:69: 34:2]) -> [async fn body@src/main.rs:32:69: 34:2] {identity_future::<String, [async fn body@src/main.rs:32:69: 34:2]>}, val: Value(<ZST>) }
    }

    bb1: {
        return;                          // scope 0 at src/main.rs:34:2: 34:2
    }
}

fn mock_instance_fun::{closure#0}(_1: Pin<&mut [async fn body@src/main.rs:32:69: 34:2]>, _2: &mut Context<'_>) -> Poll<String> {
    debug _task_context => _8;           // in scope 0 at src/main.rs:32:69: 34:2
    debug _ => ((*(_1.0: &mut [async fn body@src/main.rs:32:69: 34:2])).0: &TestStruct); // in scope 0 at src/main.rs:32:28: 32:29
    debug _data => ((*(_1.0: &mut [async fn body@src/main.rs:32:69: 34:2])).1: std::string::String); // in scope 0 at src/main.rs:32:44: 32:49
    let mut _0: std::task::Poll<std::string::String>; // return place in scope 0 at src/main.rs:32:69: 34:2
    let mut _3: &TestStruct;             // in scope 0 at src/main.rs:32:28: 32:29
    let mut _5: &str;                    // in scope 0 at src/main.rs:33:5: 33:22
    let _6: &str;                        // in scope 0 at src/main.rs:33:5: 33:11
    let mut _7: std::string::String;     // in scope 0 at src/main.rs:32:69: 34:2
    let mut _8: &mut std::task::Context<'_>; // in scope 0 at src/main.rs:32:69: 34:2
    let mut _9: u32;                     // in scope 0 at src/main.rs:32:69: 34:2
    let mut _10: &mut [async fn body@src/main.rs:32:69: 34:2]; // in scope 0 at src/main.rs:32:69: 34:2
    let mut _11: &mut [async fn body@src/main.rs:32:69: 34:2]; // in scope 0 at src/main.rs:32:69: 34:2
    let mut _12: &mut [async fn body@src/main.rs:32:69: 34:2]; // in scope 0 at src/main.rs:32:69: 34:2
    let mut _13: &mut [async fn body@src/main.rs:32:69: 34:2]; // in scope 0 at src/main.rs:32:69: 34:2
    let mut _14: &mut [async fn body@src/main.rs:32:69: 34:2]; // in scope 0 at src/main.rs:32:69: 34:2
    scope 1 {
        debug _ => _3;                   // in scope 1 at src/main.rs:32:28: 32:29
        scope 2 {
            let _4: std::string::String; // in scope 2 at src/main.rs:32:44: 32:49
            scope 3 {
                debug _data => _4;       // in scope 3 at src/main.rs:32:44: 32:49
            }
        }
    }

    bb0: {
        _10 = deref_copy (_1.0: &mut [async fn body@src/main.rs:32:69: 34:2]); // scope 0 at src/main.rs:32:69: 34:2
        _9 = discriminant((*_10));       // scope 0 at src/main.rs:32:69: 34:2
        switchInt(move _9) -> [0: bb1, 1: bb7, 2: bb6, otherwise: bb8]; // scope 0 at src/main.rs:32:69: 34:2
    }

    bb1: {
        _8 = move _2;                    // scope 0 at src/main.rs:32:69: 34:2
        _11 = deref_copy (_1.0: &mut [async fn body@src/main.rs:32:69: 34:2]); // scope 0 at src/main.rs:32:28: 32:29
        _3 = ((*_11).0: &TestStruct);    // scope 0 at src/main.rs:32:28: 32:29
        _12 = deref_copy (_1.0: &mut [async fn body@src/main.rs:32:69: 34:2]); // scope 2 at src/main.rs:32:44: 32:49
        _4 = move ((*_12).1: std::string::String); // scope 2 at src/main.rs:32:44: 32:49
        _6 = const "MOCK";               // scope 3 at src/main.rs:33:5: 33:11
                                         // mir::Constant
                                         // + span: src/main.rs:33:5: 33:11
                                         // + literal: Const { ty: &str, val: Value(Slice(..)) }
        _5 = _6;                         // scope 3 at src/main.rs:33:5: 33:22
        _7 = <str as ToOwned>::to_owned(move _5) -> [return: bb2, unwind: bb4]; // scope 3 at src/main.rs:33:5: 33:22
                                         // mir::Constant
                                         // + span: src/main.rs:33:12: 33:20
                                         // + literal: Const { ty: for<'a> fn(&'a str) -> <str as ToOwned>::Owned {<str as ToOwned>::to_owned}, val: Value(<ZST>) }
    }

    bb2: {
        drop(_4) -> [return: bb3, unwind: bb5]; // scope 2 at src/main.rs:34:1: 34:2
    }

    bb3: {
        Deinit(_0);                      // scope 0 at src/main.rs:34:2: 34:2
        ((_0 as Ready).0: std::string::String) = move _7; // scope 0 at src/main.rs:34:2: 34:2
        discriminant(_0) = 0;            // scope 0 at src/main.rs:34:2: 34:2
        _13 = deref_copy (_1.0: &mut [async fn body@src/main.rs:32:69: 34:2]); // scope 0 at src/main.rs:34:2: 34:2
        discriminant((*_13)) = 1;        // scope 0 at src/main.rs:34:2: 34:2
        return;                          // scope 0 at src/main.rs:34:2: 34:2
    }

    bb4 (cleanup): {
        drop(_4) -> bb5;                 // scope 2 at src/main.rs:34:1: 34:2
    }

    bb5 (cleanup): {
        _14 = deref_copy (_1.0: &mut [async fn body@src/main.rs:32:69: 34:2]); // scope 0 at src/main.rs:32:69: 34:2
        discriminant((*_14)) = 2;        // scope 0 at src/main.rs:32:69: 34:2
        resume;                          // scope 0 at src/main.rs:32:69: 34:2
    }

    bb6: {
        assert(const false, "`async fn` resumed after panicking") -> bb6; // scope 0 at src/main.rs:32:69: 34:2
    }

    bb7: {
        assert(const false, "`async fn` resumed after completion") -> bb7; // scope 0 at src/main.rs:32:69: 34:2
    }

    bb8: {
        unreachable;                     // scope 0 at src/main.rs:32:69: 34:2
    }
}

```

It's not clear exactly what you're doing - in particular how mock::stub_async_func! works since you haven't provided the definition of that macro - but keep in mind that two different async functions will return two different types, even if the functions have the same signature. ie. The impl Future returned by TestStruct::instance_method and the impl Future returned by mock_instance_fun are two different opaque types which both implement Future.

If you're overwriting the machine code for TestStruct::instance_method with a jump instruction to mock_instance_fun then you're making TestStruct::instance_method return the wrong type. That is definitely undefined behaviour. For it to have any chance of working in practice you'd at least need to:

(a) overwrite the poll and drop methods for the opaque Future type of TestStruct::instance_method.

(b) make sure that no calls to poll/drop are getting inlined.

(c) make sure that the two future types have the same size.

(d) make sure that the replacement future type has less restrictive alignment, auto-trait and lifetime requirements.

(e) probably address a bunch of other problems I can't think of right now.

This sort of thing is definitely not supported though. Even if you happen to get it working, any future updates to the compiler are free to break it again. You'd be much better off trying to work within the language, leveraging the type system and macro system to achieve your goals.

1 Like

Yes, the most critical problem is that the Future object returned by async fn is inconsistent. I haven't found a good way to do this, like using macros.

Who knows how to get the pointer of the drop function from impl Future<Output = String>?

  fn mock_instance_fun(_1: &TestStruct, _2: String) -> impl Future<Output = String> {

There is not guaranteed to be a "drop function" for impl Future<Output = String> - the compiler is permitted to inline that function in all call sites, and therefore you cannot get a pointer to it.

Fundamentally, if you're modifying the generated machine code, you're in a place where things might or might not work depending on the compiler version, compiler flags and other things outside your control - the only reason the Go library you pointed to works at all is that it targets a relatively unsophisticated version of the Go compiler, and indeed the library's own README asks you to turn off some optimizations in order to make it work at all.

2 Likes

Thank you for your professional reply. Is there any way in the community that developers can write async fn UTs as easily as any other language? Like the monkey I listed, mockall can't solve a scenario like this.

I don't know what you mean by a "UT", so I can't answer that question.

In general, when I'm writing test cases for an async fn, I'm using Tokio anyway, and thus I use the tokio::test attribute to make my test function work:

#[tokio::test]
async fn test_something() -> anyhow::Result<()>

and if I need a mock of something, I will implement it manually - I find that I rarely need mocks, and when I do, I need them to have more complex behaviour than an automatic implementation can provide (e.g. I find myself needing a mock that behaves the way real hardware has been observed to behave, not one that simply has the same type signature).

Where realistic, structure code to be sans io, so it's possible to test without relying on doing IO. If you're generic over Read/Write (or the async equivalents), this is fairly simple by using the in-memory buffer implementations (e.g. Cursor).

Mocking via direct monkeypatching of upstream dependencies effectively isn't possible in Rust. It's also generally not a great idea, since it necessarily relies on some amount of global state to determine what results should be mocked, and Rust tests are set up to be run in parallel by default.

Instead, mocks are better done with dependency injection, either by traits (e.g. via mockall) or structs (e.g. via faux). faux supports mocking async methods, so if that's what you're mocking (rather than associated or free functions), then it should be sufficient. I've not done anything requiring mocking for effective testing, but utilizing mockall for traits and faux for structs seems sufficient. (Though I'd probably investigate using faux for mocking traits as well, to only have one mocking API to interact with; this should be reasonable in theory.)

If you really want to mock functions without a self type to mock, reconsider. But if you still need to, it'd be done by proxying through a global object which you can mock.

I'd suggest reading Matklad's How to Test. If you need mocks for effective testing (that isn't a mock filesystem/network environment), there's something suboptimal in your software architecture. Rust-analyzer's tests run plenty quickly, despite being almost entirely end-to-end integration tests, and without any mocking, because the majority of rust-analyzer can be exercised sans-io, and as a result the IO glue layer is thin and doesn't require that much testing.

(And, if a topic is about developing in Rust rather than evolving Rust itself, it's generally a better fit for the users forum than this, the internals forum. Abusing implementation details and/or queries about stability thereof is a somewhat grey area, and it depends on the class of answer you want; users leans more "here's an answer that works now;" internals means more "here's how this could work three years from now.")

6 Likes

faux should be a very good choice, I'll try