@@ -291,25 +291,33 @@ pub(crate) fn set<'a>(
291291 throw : bool ,
292292 mut gc : GcScope <'a , '_ >,
293293) -> JsResult <'a , ()> {
294+ // By convention, always bind all parameters at function entry for safety.
294295 let nogc = gc . nogc ();
295296 let o = o . bind (nogc );
296297 let p = p . bind (nogc );
297298 let v = v . bind (nogc );
299+
300+ // Scope p for use after possible garbage collection safepoint.
298301 let scoped_p = p . scope (agent , nogc );
302+
303+ // Actual function work starts.
299304 let success = o
300305 . unbind ()
301306 . internal_set (agent , p . unbind (), v . unbind (), o . unbind (). into (), gc . reborrow ())
302307 . unbind ()? ;
308+
309+ // Transition to a proven "no GC past this point" regime.
303310 let gc = gc . into_nogc ();
304- let p = scoped_p . get (agent ). bind (gc );
305311 if ! success && throw {
312+ let p = scoped_p . get (agent ). bind (gc );
306313 return throw_set_error (agent , p , gc ). into ();
307314 }
308315 Ok (())
309316}
310317
311318/// The unbind() and bind() functions come from this trait.
312319unsafe trait Bindable {
320+ /// This is always Self<'a>;
313321 type Of <'a >;
314322
315323 fn unbind (self ) -> Self :: Of <'static >;
@@ -344,23 +352,56 @@ pub(crate) fn set<'a>(
344352 throw : bool ,
345353 mut gc : GcScope <'a >,
346354) -> JsResult <'a , ()> {
355+ // We should still "bind" all parameters at function entry for safety.
347356 let nogc = gc . nogc ();
348357 let o = o . local ();
349358 nogc . join (o );
350359 let p = p . local ();
351360 nogc . join (p );
352361 let v = v . local ();
353362 nogc . join (v );
363+
364+ // We still have to scope p.
354365 let scoped_p = p . scope (agent , nogc );
366+
367+ // Actual function work starts.
355368 let success = o . internal_set (agent , p , v , o . into (), gc . reborrow ())? ;
369+
370+ // It's still useful to mark "no GC" regimes explicitly.
356371 let gc = gc . into_nogc ();
357- let p = scoped_p . get (agent );
358- gc . join (p );
359372 if ! success && throw {
373+ let p = scoped_p . get (agent );
374+ gc . join (p );
360375 return Err (throw_set_error (agent , p , gc ));
361376 }
362377 Ok (())
363378}
379+
380+ /// Helper functions
381+ trait Handle {
382+ /// This is always Self<'a>;
383+ type Of <'a >;
384+
385+ /// Create a local copy of Self: note that for contravariant lifetimes this
386+ /// is fundamentally an unsafe operation.
387+ fn local <'a >(& 'a self ) -> Self :: Of <'a >;
388+ }
389+
390+ impl <'gc , 'scope > GcScope <'gc , 'scope > {
391+ /// Join a handle's lifetime together with a shared borrow of the
392+ /// (guaranteed unique) GcScope.
393+ #[inline(always)]
394+ fn join <'a , T : Handle >(& 'a self , handle : T :: Of <'a >) {}
395+ }
396+
397+ impl <'gc , 'scope > NoGcScope <'gc , 'scope > {
398+ /// Join a handle's lifetime together with the GC lifetime of a GcScope.
399+ /// Note that NoGcScope is a Copy type created from a shared borrow of the
400+ /// (guaranteed unique) GcScope, meaning that this joins the handle's
401+ /// lifetime together with a shared borrow of that GcScope.
402+ #[inline(always)]
403+ fn join <T : Handle >(self , handle : T :: Of <'gc >) {}
404+ }
364405```
365406
366407The most important change here is the actual ` internal_set ` call: the
0 commit comments