One of the benefits of using AutoCheckCannotGC is that the static analysis that checks for unrooted pointers also checks that GC doesn't occur within the scope of an AutoCheckCannotGC. Static checks are vastly preferable to dynamic checks, but JS::ubi::RootList's use of a Maybe<AutoCheckCannotGC> instead of a direct AutoCheckCannotGC causes the static analysis to ignore the contained instance, as its lifetime is not stack-like, leaving us with only the dynamic checks that AutoCheckCannotGC itself performs. JS::ubi::RootList's job is to build an array of ubi::Node values referring to a runtime's roots. Since ubi::Nodes are not stable across GCs, we want to use an AutoCheckCannotGC to be sure our array doesn't go stale. However, the operation RootList uses to collect the roots, JS_TraceRuntime, itself performs a minor GC to evacuate the nursery, before doing an interior iteration over the roots. Because the minor GC and the iteration are done by the same function, we can't construct an AutoCheckCannotGC in between those two steps, to properly enclose the ubi::Nodes' lifetimes. If we split JS_TraceRuntime into two functions --- one which evacuates the nursery; and a second which actually walks the roots, asserting that the nursery is empty on entry --- then we could construct a non-Maybe AutoCheckCannotGC between those two calls, and recover the benefits of static analysis.