How would you assert dynamically that if an interval is reachable, its
owning string or buffer must be too?
You don't. You check it statically (by a human).
It's not enough for the variable holding the reference to the string
or buffer to be in scope: you have to be sure that the reference
isn't dead.
It should be: if it's in scope, it's not dead.
That's not the case.
In general, no, but in this specific case I think it always will.
E.g. because in order to be in the process of working on the intervals of
a buffer, that buffer needs to be not just reachable but buffer-live-p,
so you'd have to mess with buffer-alist before the buffer can be
reclaimed, which is highly unlikely to happen within the functions that
manipulate intervals.
For strings, the argument might not be as strong. Maybe code like
(ignore (propertize "foo" 'a 'b)) could lead to us working on an
unreachable string, so it could get GC'd while we manipulate its
intervals.
So for those cases, I guess the main safety argument we have is that we
will not call the GC while we're in the middle of manipulating struct
interval objects.