@@ -1003,9 +1003,38 @@ impl FrameKind {
10031003 }
10041004}
10051005
1006+ /// Unique right to mutably run an interpreter frame in the trampoline.
1007+ ///
1008+ /// Not `Copy`: two handles to the same allocation would let two
1009+ /// `&mut InterpreterFrame` exist at once. `from_mut` consumes an exclusive
1010+ /// borrow; `from_ptr` is `unsafe` and must not alias another live handle.
1011+ /// Pointer validity (datastack LIFO, generator `PyRef` + running claim) is
1012+ /// still a construction contract, not something this type can prove.
1013+ struct TrampolineIFrame {
1014+ ptr : * mut crate :: frame:: InterpreterFrame ,
1015+ }
1016+
1017+ impl TrampolineIFrame {
1018+ fn from_mut ( iframe : & mut crate :: frame:: InterpreterFrame ) -> Self {
1019+ Self { ptr : iframe }
1020+ }
1021+
1022+ /// # Safety
1023+ /// `ptr` must point to a live frame, and no other `TrampolineIFrame`
1024+ /// may alias it until this handle is dropped.
1025+ unsafe fn from_ptr ( ptr : * mut crate :: frame:: InterpreterFrame ) -> Self {
1026+ Self { ptr }
1027+ }
1028+
1029+ fn as_mut ( & mut self ) -> & mut crate :: frame:: InterpreterFrame {
1030+ // SAFETY: unique handle; construction established the pointer.
1031+ unsafe { & mut * self . ptr }
1032+ }
1033+ }
1034+
10061035/// Caller frame suspended by a TailCall in the trampoline.
10071036struct SuspendedFrame {
1008- iframe : * mut crate :: frame :: InterpreterFrame ,
1037+ iframe : TrampolineIFrame ,
10091038 kind : FrameKind ,
10101039 /// Function that owns the callee's raw pointers (code, globals, builtins,
10111040 /// closure, and func_obj). Moved from `vm.pending_tailcall_owner` when the
@@ -1057,7 +1086,7 @@ enum GenEntered {
10571086 /// The generator at the bottom of the chain ran and produced `result`;
10581087 /// `state` and `iframe` are its own.
10591088 Ran {
1060- iframe : * mut crate :: frame :: InterpreterFrame ,
1089+ iframe : TrampolineIFrame ,
10611090 state : crate :: coroutine:: FlatResume ,
10621091 result : PyResult < crate :: frame:: ExecutionResult > ,
10631092 } ,
@@ -1855,11 +1884,11 @@ impl VirtualMachine {
18551884
18561885 /// Free a callee frame's data stack storage, if it still owns any.
18571886 #[ inline]
1858- fn release_trampoline_callee ( & self , iframe : * mut crate :: frame :: InterpreterFrame ) {
1887+ fn release_trampoline_callee ( & self , mut iframe : TrampolineIFrame ) {
18591888 // SAFETY: the callee has finished; its storage is the top of this
18601889 // thread's data stack because frames are released in LIFO order.
18611890 unsafe {
1862- if let Some ( ( base, size) ) = ( * iframe) . release_datastack_frame ( ) {
1891+ if let Some ( ( base, size) ) = iframe. as_mut ( ) . release_datastack_frame ( ) {
18631892 self . datastack_pop_frame ( base, size) ;
18641893 }
18651894 }
@@ -1888,15 +1917,15 @@ impl VirtualMachine {
18881917 }
18891918 Err ( exc) => return GenEntered :: Failed ( Outcome :: Raise ( exc) ) ,
18901919 } ;
1891- let iframe = state. iframe_ptr ( ) ;
18921920 // SAFETY: the frame is linked and claimed, so this thread is its
1893- // only executor for as long as the record below lives.
1894- let frame = unsafe { & mut * iframe } ;
1921+ // only executor for as long as the handle below lives.
1922+ let mut iframe = unsafe { TrampolineIFrame :: from_ptr ( state . iframe_ptr ( ) ) } ;
18951923 if let Some ( sent) = sent {
1896- if let Some ( ( delegate, cont) ) = crate :: frame:: yield_from_delegate ( frame, self )
1924+ if let Some ( ( delegate, cont) ) =
1925+ crate :: frame:: yield_from_delegate ( iframe. as_mut ( ) , self )
18971926 && crate :: frame:: gen_collapse_allowed ( self )
18981927 {
1899- crate :: frame:: park_at_send ( frame , cont) ;
1928+ crate :: frame:: park_at_send ( iframe . as_mut ( ) , cont) ;
19001929 self . trampoline_push ( SuspendedFrame {
19011930 iframe,
19021931 kind : FrameKind :: Gen ( state) ,
@@ -1907,12 +1936,14 @@ impl VirtualMachine {
19071936 value = sent;
19081937 continue ;
19091938 }
1910- frame . localsplus . push_stack ( sent) ;
1939+ iframe . as_mut ( ) . localsplus . push_stack ( sent) ;
19111940 }
1941+ let result =
1942+ crate :: frame:: run_iframe ( iframe. as_mut ( ) , crate :: frame:: Flatten :: GenResume , self ) ;
19121943 return GenEntered :: Ran {
19131944 iframe,
19141945 state,
1915- result : crate :: frame :: run_iframe ( frame , crate :: frame :: Flatten :: GenResume , self ) ,
1946+ result,
19161947 } ;
19171948 }
19181949 }
@@ -1938,17 +1969,19 @@ impl VirtualMachine {
19381969 #[ inline( never) ]
19391970 fn run_trampoline (
19401971 & self ,
1941- iframe : * mut crate :: frame:: InterpreterFrame ,
1972+ iframe : & mut crate :: frame:: InterpreterFrame ,
19421973 kind : FrameKind ,
19431974 start : TrampolineStart ,
19441975 ) -> PyResult < ExecutionResult > {
19451976 use crate :: frame:: ExecutionResult ;
19461977
1978+ let iframe = TrampolineIFrame :: from_mut ( iframe) ;
1979+
19471980 /// What the loop does next. Deliberately small, and holding no frame
19481981 /// state: every ordinary Python-to-Python call passes through here.
19491982 enum Action {
19501983 /// Enter and run the data stack frame a `TailCall` prepared.
1951- EnterCallee ( * mut crate :: frame :: InterpreterFrame ) ,
1984+ EnterCallee ( TrampolineIFrame ) ,
19521985 /// Resume the generator a `GenResume` parked, walking down its
19531986 /// `yield from` chain.
19541987 ResumeGen {
@@ -1975,14 +2008,17 @@ impl VirtualMachine {
19752008 match $result {
19762009 Ok ( ExecutionResult :: TailCall ) => {
19772010 let callee_owner = self . take_pending_tailcall_owner( ) ;
1978- let callee_ptr = self . take_pending_tailcall( ) ;
2011+ // SAFETY: the callee was just allocated on this
2012+ // thread's data stack; this is the first handle.
2013+ let callee =
2014+ unsafe { TrampolineIFrame :: from_ptr( self . take_pending_tailcall( ) ) } ;
19792015 self . trampoline_push( SuspendedFrame {
19802016 iframe,
19812017 kind,
19822018 callee_owner: Some ( callee_owner) ,
19832019 cont: crate :: frame:: GenCont :: NONE ,
19842020 } ) ;
1985- Action :: EnterCallee ( callee_ptr )
2021+ Action :: EnterCallee ( callee )
19862022 }
19872023 Ok ( ExecutionResult :: GenResume ) => {
19882024 let PendingGenResume { jen, value, cont } = self . take_pending_gen_resume( ) ;
@@ -2057,21 +2093,18 @@ impl VirtualMachine {
20572093
20582094 loop {
20592095 action = match action {
2060- Action :: EnterCallee ( callee_ptr) => {
2061- // SAFETY: the callee frame was just allocated on this
2062- // thread's data stack, and nothing has popped it.
2063- let callee = unsafe { & mut * callee_ptr } ;
2064- match self . enter_iframe_unchecked ( callee) {
2096+ Action :: EnterCallee ( mut callee) => {
2097+ match self . enter_iframe_unchecked ( callee. as_mut ( ) ) {
20652098 Ok ( state) => {
20662099 let result = crate :: frame:: run_iframe (
2067- callee,
2100+ callee. as_mut ( ) ,
20682101 crate :: frame:: Flatten :: CallAndGenResume ,
20692102 self ,
20702103 ) ;
2071- dispatch ! ( callee_ptr , FrameKind :: Callee ( state) , result)
2104+ dispatch ! ( callee , FrameKind :: Callee ( state) , result)
20722105 }
20732106 Err ( exc) => {
2074- self . release_trampoline_callee ( callee_ptr ) ;
2107+ self . release_trampoline_callee ( callee ) ;
20752108 Action :: Deliver ( Outcome :: Raise ( exc) )
20762109 }
20772110 }
@@ -2091,7 +2124,7 @@ impl VirtualMachine {
20912124 // frame it has already suspended, so an outcome always has
20922125 // a caller waiting for it.
20932126 let SuspendedFrame {
2094- iframe,
2127+ mut iframe,
20952128 kind,
20962129 callee_owner,
20972130 cont,
@@ -2105,16 +2138,13 @@ impl VirtualMachine {
21052138 // than holding it across the caller's next stretch of
21062139 // bytecode.
21072140 drop ( callee_owner) ;
2108- // SAFETY: a suspended caller's frame stays alive and
2109- // unmoved until it is resumed here.
2110- let frame = unsafe { & mut * iframe } ;
21112141 match outcome {
21122142 // A frame parked mid `yield from` re-yields what its
21132143 // delegate produced, running no instruction of its own.
21142144 Outcome :: Value ( value)
21152145 if cont. is_some ( ) && crate :: frame:: gen_collapse_allowed ( self ) =>
21162146 {
2117- crate :: frame:: park_after_yield_from ( frame , cont. resumed_at ) ;
2147+ crate :: frame:: park_after_yield_from ( iframe . as_mut ( ) , cont. resumed_at ) ;
21182148 match kind {
21192149 FrameKind :: Gen ( state) => {
21202150 Action :: Deliver ( self . trampoline_finish_gen (
@@ -2133,28 +2163,34 @@ impl VirtualMachine {
21332163 }
21342164 }
21352165 Outcome :: Value ( value) => {
2136- frame . localsplus . push_stack ( value) ;
2137- let result = self . trampoline_run ( frame , & kind) ;
2166+ iframe . as_mut ( ) . localsplus . push_stack ( value) ;
2167+ let result = self . trampoline_run ( iframe . as_mut ( ) , & kind) ;
21382168 dispatch ! ( iframe, kind, result)
21392169 }
21402170 Outcome :: GenStop ( value) => {
21412171 debug_assert ! (
21422172 cont. is_some( ) ,
21432173 "a generator finished with no continuation to apply"
21442174 ) ;
2145- let result =
2146- match crate :: frame:: trampoline_gen_stop ( frame, value, cont, self ) {
2147- Ok ( ( ) ) => self . trampoline_run ( frame, & kind) ,
2148- Err ( exc) => Err ( exc) ,
2149- } ;
2175+ let result = match crate :: frame:: trampoline_gen_stop (
2176+ iframe. as_mut ( ) ,
2177+ value,
2178+ cont,
2179+ self ,
2180+ ) {
2181+ Ok ( ( ) ) => self . trampoline_run ( iframe. as_mut ( ) , & kind) ,
2182+ Err ( exc) => Err ( exc) ,
2183+ } ;
21502184 dispatch ! ( iframe, kind, result)
21512185 }
21522186 Outcome :: Raise ( exc) => {
21532187 let result = match crate :: frame:: trampoline_handle_exception (
2154- frame, & exc, self ,
2188+ iframe. as_mut ( ) ,
2189+ & exc,
2190+ self ,
21552191 ) {
21562192 // Handler found — resume the caller's loop.
2157- Ok ( None ) => self . trampoline_run ( frame , & kind) ,
2193+ Ok ( None ) => self . trampoline_run ( iframe . as_mut ( ) , & kind) ,
21582194 Ok ( Some ( result) ) => Ok ( result) ,
21592195 Err ( exc) => Err ( exc) ,
21602196 } ;
0 commit comments