Have a static checker prove that no Weak upgrade or Rc borrow_mut can fail.
Static checking across function boundaries will either require an analyzer that infers a lot automatically (which probably would be very slow and lead to spooky action at a distance) or aid from the language (such as lifetime annotations). So ISTM that you're asking for a more powerful lifetime system? I assume alternatives like arenas or garbage collectors are out?
There has been a cluster of blog-posts by various people about improving the situation for self-referential data (e.g. Further simplifying self-referential types for Rust) recently, often in the context of async code but this should also be useful in other contexts. But your problem statement is general enough that it's hard to tell if that would be sufficient for you or if you'd need something even more powerful.
It's more ambitious than what I had in mind. They want objects with back-references to still be moveable. That's hard, because something has to update the back-references. So they need move constructors and more heavy machinery to make that go.
I'd settle for pinning objects with back-references. That makes this much simpler.
The global analysis required is much like scope oriented borrow checking. The basic rule is that you can't have two conflicting references in active use at the same time. That's a scope overlap kind of problem.
I need to work up some examples. You can write back references now with Rc, RefCell, Weak, upgrade, borrow, and borrow_mut. It's hard to tell if you're going to have a failed upgrade or borrow at run-time, which is a source of bugs. The goal is to check that at compile time.
Backreferences are fundamentally in contention with a core proposition of the language (everything is movable), so don't hold your breath. They should be the exception rather than the rule when designing your data structures, therefore, given good enough workarounds (such as Weak or index-based indirection), the enormous added complexity and overhaul required to support them as a language feature is 100% "the juice is not worth the squeeze" territory.
It depends on what the actual problem is. The problem description is overly general because back-references (i.e. cycles) essentially amounts to fully automatic memory management (e.g. garbage collection).
Constrained subsets of the problem may be more tractable with extensions to borrowchecking such as better support for self-referential types, ghost-cell or maybe Vale's region-based borrow checking (though I'm not sure if I understand correctly where it gets its claimed advantages).
The Ghost Cell and Static Rc approaches do roughly the right thing. But both projects are abandoned. The European Union funding ran out.
static-rc has the key idea: "the exact number of aliases is known at compile-time". That's enough to handle the case where A owns B and B needs a back-reference to A.
Anyone looked at that? It's experimental and abandoned, but seems to be a good idea.