@@ -17,24 +17,22 @@ use rustc_target::spec::PanicStrategy;
1717
1818use crate :: * ;
1919
20- /// Holds all of the relevant data for a call to
21- /// `__rust_maybe_catch_panic`.
22- ///
23- /// If a panic occurs, we update this data with
24- /// the information from the panic site.
20+ /// Holds all of the relevant data for when unwinding hits a `try` frame.
2521#[ derive( Debug ) ]
2622pub struct CatchUnwindData < ' tcx > {
27- /// The dereferenced `data_ptr` argument passed to `__rust_maybe_catch_panic`.
28- pub data_place : MPlaceTy < ' tcx , Tag > ,
29- /// The dereferenced `vtable_ptr` argument passed to `__rust_maybe_catch_panic`.
30- pub vtable_place : MPlaceTy < ' tcx , Tag > ,
31- /// The `dest` from the original call to `__rust_maybe_catch_panic`.
32- pub dest : PlaceTy < ' tcx , Tag > ,
23+ /// The `catch_fn` callback to call in case of a panic.
24+ catch_fn : Scalar < Tag > ,
25+ /// The `data` argument for that callback.
26+ data : Scalar < Tag > ,
27+ /// The return place from the original call to `try`.
28+ dest : PlaceTy < ' tcx , Tag > ,
29+ /// The return block from the original call to `try`.
30+ ret : mir:: BasicBlock ,
3331}
3432
3533impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
3634pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
37- /// Handles the special " miri_start_panic" intrinsic, which is called
35+ /// Handles the special ` miri_start_panic` intrinsic, which is called
3836 /// by libpanic_unwind to delegate the actual unwinding process to Miri.
3937 fn handle_miri_start_panic (
4038 & mut self ,
@@ -46,47 +44,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4644 trace ! ( "miri_start_panic: {:?}" , this. frame( ) . span) ;
4745
4846 // Get the raw pointer stored in arg[0] (the panic payload).
49- let scalar = this. read_immediate ( args[ 0 ] ) ?;
47+ let payload = this. read_scalar ( args[ 0 ] ) ? . not_undef ( ) ?;
5048 assert ! (
5149 this. machine. panic_payload. is_none( ) ,
5250 "the panic runtime should avoid double-panics"
5351 ) ;
54- this. machine . panic_payload = Some ( scalar ) ;
52+ this. machine . panic_payload = Some ( payload ) ;
5553
5654 // Jump to the unwind block to begin unwinding.
5755 this. unwind_to_block ( unwind) ;
5856 return Ok ( ( ) ) ;
5957 }
6058
61- fn handle_catch_panic (
59+ /// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`.
60+ fn handle_try (
6261 & mut self ,
6362 args : & [ OpTy < ' tcx , Tag > ] ,
6463 dest : PlaceTy < ' tcx , Tag > ,
6564 ret : mir:: BasicBlock ,
6665 ) -> InterpResult < ' tcx > {
6766 let this = self . eval_context_mut ( ) ;
68- let tcx = & { this. tcx . tcx } ;
6967
70- // fn __rust_maybe_catch_panic(
71- // f: fn(*mut u8),
72- // data: *mut u8,
73- // data_ptr: *mut usize,
74- // vtable_ptr: *mut usize,
75- // ) -> u32
68+ // Signature:
69+ // fn r#try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32
70+ // Calls `try_fn` with `data` as argument. If that executes normally, returns 0.
71+ // If that unwinds, calls `catch_fn` with the first argument being `data` and
72+ // then second argument being a target-dependent `payload` (i.e. it is up to us to define
73+ // what that is), and returns 1.
74+ // The `payload` is passed (by libstd) to `__rust_panic_cleanup`, which is then expected to
75+ // return a `Box<dyn Any + Send + 'static>`.
76+ // In Miri, `miri_start_panic` is passed exactly that type, so we make the `payload` simply
77+ // a pointer to `Box<dyn Any + Send + 'static>`.
7678
7779 // Get all the arguments.
78- let f = this. read_scalar ( args[ 0 ] ) ?. not_undef ( ) ?;
79- let f_arg = this. read_scalar ( args[ 1 ] ) ?. not_undef ( ) ?;
80- let data_place = this. deref_operand ( args[ 2 ] ) ?;
81- let vtable_place = this. deref_operand ( args[ 3 ] ) ?;
82-
83- // Now we make a function call, and pass `f_arg` as first and only argument.
84- let f_instance = this. memory . get_fn ( f) ?. as_instance ( ) ?;
85- trace ! ( "__rust_maybe_catch_panic: {:?}" , f_instance) ;
86- let ret_place = MPlaceTy :: dangling ( this. layout_of ( tcx. mk_unit ( ) ) ?, this) . into ( ) ;
80+ let try_fn = this. read_scalar ( args[ 0 ] ) ?. not_undef ( ) ?;
81+ let data = this. read_scalar ( args[ 1 ] ) ?. not_undef ( ) ?;
82+ let catch_fn = this. read_scalar ( args[ 2 ] ) ?. not_undef ( ) ?;
83+
84+ // Now we make a function call, and pass `data` as first and only argument.
85+ let f_instance = this. memory . get_fn ( try_fn) ?. as_instance ( ) ?;
86+ trace ! ( "try_fn: {:?}" , f_instance) ;
87+ let ret_place = MPlaceTy :: dangling ( this. layout_of ( this. tcx . mk_unit ( ) ) ?, this) . into ( ) ;
8788 this. call_function (
8889 f_instance,
89- & [ f_arg . into ( ) ] ,
90+ & [ data . into ( ) ] ,
9091 Some ( ret_place) ,
9192 // Directly return to caller.
9293 StackPopCleanup :: Goto { ret : Some ( ret) , unwind : None } ,
@@ -95,12 +96,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
9596 // We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
9697 this. write_null ( dest) ?;
9798
98- // In unwind mode, we tag this frame with some extra data.
99+ // In unwind mode, we tag this frame with the extra data needed to catch unwinding .
99100 // This lets `handle_stack_pop` (below) know that we should stop unwinding
100101 // when we pop this frame.
101- if this. tcx . tcx . sess . panic_strategy ( ) == PanicStrategy :: Unwind {
102- this. frame_mut ( ) . extra . catch_panic =
103- Some ( CatchUnwindData { data_place, vtable_place, dest } )
102+ if this. tcx . sess . panic_strategy ( ) == PanicStrategy :: Unwind {
103+ this. frame_mut ( ) . extra . catch_unwind = Some ( CatchUnwindData { catch_fn, data, dest, ret } ) ;
104104 }
105105
106106 return Ok ( ( ) ) ;
@@ -110,45 +110,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
110110 & mut self ,
111111 mut extra : FrameData < ' tcx > ,
112112 unwinding : bool ,
113- ) -> InterpResult < ' tcx , StackPopInfo > {
113+ ) -> InterpResult < ' tcx , StackPopJump > {
114114 let this = self . eval_context_mut ( ) ;
115115
116116 trace ! ( "handle_stack_pop(extra = {:?}, unwinding = {})" , extra, unwinding) ;
117+ if let Some ( stacked_borrows) = this. memory . extra . stacked_borrows . as_ref ( ) {
118+ stacked_borrows. borrow_mut ( ) . end_call ( extra. call_id ) ;
119+ }
117120
118121 // We only care about `catch_panic` if we're unwinding - if we're doing a normal
119122 // return, then we don't need to do anything special.
120- let res = if let ( true , Some ( unwind_data ) ) = ( unwinding, extra. catch_panic . take ( ) ) {
121- // We've just popped a frame that was pushed by `__rust_maybe_catch_panic `,
123+ if let ( true , Some ( catch_unwind ) ) = ( unwinding, extra. catch_unwind . take ( ) ) {
124+ // We've just popped a frame that was pushed by `try `,
122125 // and we are unwinding, so we should catch that.
123126 trace ! ( "unwinding: found catch_panic frame during unwinding: {:?}" , this. frame( ) . span) ;
124127
125- // `panic_payload` now holds a `*mut (dyn Any + Send)`,
126- // provided by the `miri_start_panic` intrinsic.
127- // We want to split this into its consituient parts -
128- // the data and vtable pointers - and store them according to
129- // `unwind_data`, i.e., we store them where `__rust_maybe_catch_panic`
130- // was told to put them.
131- let payload = this. machine . panic_payload . take ( ) . unwrap ( ) ;
132- let payload = this. ref_to_mplace ( payload) ?;
133- let payload_data_place = payload. ptr ;
134- let payload_vtable_place = payload. meta . unwrap_meta ( ) ;
135-
136- this. write_scalar ( payload_data_place, unwind_data. data_place . into ( ) ) ?;
137- this. write_scalar ( payload_vtable_place, unwind_data. vtable_place . into ( ) ) ?;
128+ // We set the return value of `try` to 1, since there was a panic.
129+ this. write_scalar ( Scalar :: from_i32 ( 1 ) , catch_unwind. dest ) ?;
138130
139- // We set the return value of `__rust_maybe_catch_panic` to 1,
140- // since there was a panic.
141- let dest = unwind_data. dest ;
142- this. write_scalar ( Scalar :: from_int ( 1 , dest. layout . size ) , dest) ?;
131+ // `panic_payload` holds what was passed to `miri_start_panic`.
132+ // This is exactly the second argument we need to pass to `catch_fn`.
133+ let payload = this. machine . panic_payload . take ( ) . unwrap ( ) ;
143134
144- StackPopInfo :: StopUnwinding
135+ // Push the `catch_fn` stackframe.
136+ let f_instance = this. memory . get_fn ( catch_unwind. catch_fn ) ?. as_instance ( ) ?;
137+ trace ! ( "catch_fn: {:?}" , f_instance) ;
138+ let ret_place = MPlaceTy :: dangling ( this. layout_of ( this. tcx . mk_unit ( ) ) ?, this) . into ( ) ;
139+ this. call_function (
140+ f_instance,
141+ & [ catch_unwind. data . into ( ) , payload. into ( ) ] ,
142+ Some ( ret_place) ,
143+ // Directly return to caller of `try`.
144+ StackPopCleanup :: Goto { ret : Some ( catch_unwind. ret ) , unwind : None } ,
145+ ) ?;
146+
147+ // We pushed a new stack frame, the engine should not do any jumping now!
148+ Ok ( StackPopJump :: NoJump )
145149 } else {
146- StackPopInfo :: Normal
147- } ;
148- if let Some ( stacked_borrows) = this. memory . extra . stacked_borrows . as_ref ( ) {
149- stacked_borrows. borrow_mut ( ) . end_call ( extra. call_id ) ;
150+ Ok ( StackPopJump :: Normal )
150151 }
151- Ok ( res)
152152 }
153153
154154 /// Starta a panic in the interpreter with the given message as payload.
0 commit comments