I've looked at this a bit, and it seems that every failure has to do with dynamic-extent objects in arglists. Backtrace calls REPLACE-DYNAMIC-EXTENT-OBJECTS but there are dynamic-extent objects inside heap objects, so it does not replace the culprits.
As soon as I make one function "immune" to bad DX objects, and rerun the sample code, another function turns up that crashes. And this boring chore of making them immune is both terrible for semantics and performance. All the objects that happen to cause crashing are INSTANCE types (luckily, I would say), so I've added checks that any pointer into the stack tagged as instance-pointer-lowtag points to a word with instance-header-widetag, and that the layout-of is also an instance. I've done this in things like %FUN-NAME and OUTPUT-UGLY-OBJECT, doing something like returning NIL if the checks fail. This is horrible.
Now it's quite clear that this test case is mostly just exercising the compiler; so my questions about that are several:
- Do we believe that it is possible to write code which respects the concept of dynamic-extent such that a function which receives a DX arg is on the stack, but the arglists somehow contains a heap object pointing to an object in a frame that exited, or otherwise reused those stack words? (which is exactly what seems to be happening)
- Do we believe that this interesting pattern is part of SBCL itself and fairly widespread?
- If not part of the compiler, do we believe that users can write macroexpanders and such that it makes it look like it's in the compiler?
- What does this have to do with GC?
TLDR: it seems that under the wrong circumstances, it's impossible to backtrace through the compiler because many dynamic-extent objects are buried in heap objects, and the frames that created the DX objects have exited. Is this legal?