I think trying to detect reentrancy around a primitive where reentrancy is UB is just a waste of time. It is a lot of work, complex code that can go wrong, and likely horrible for performance.
On Windows, even reentrant read locks of a RwLock are UB! TBH I can only consider such a primitive broken, and the best one can do with it is avoid it. On macOS, “only” recursive write locking of an RwLock is UB.
On Linux, we are actually fine because pthreads there says recursive RwLock is okay, and for Mutex we go through great lengths to set the required flags and not move stuff. Unfortunately some parts of libstd have to use the non-reentrant-safe mutex because they need a const initializer, so this is still far from a great situation but at least it is not UB (hoping no of the libstd internal users screwed up).
Given the rather shocking situation that system-level concurrency primitives are in, I am fully in favor of rolling our own implementation. We already have park/unpark in libstd, which only needs a condvar. So we could restrict our platform-specific implementations to just what is needed for a basic condvar (and it’d likely be unsafe because we’d use a non-reentrant-safe mutex, but it only has one user so that seems okay), and then build the rest on top of that. Since parking_lot does pretty much that, we probably do not even have to figure out what this API provided by libstd::sys should look like. 
I imagine that would be the ThreadParker @Amanieu mentioned? That’s on a higher level than I expected, i.e. it seems to do everything park/unpark do instead of a shared implementation using even lower-level primitives that then have platform-specific implementations. @Amanieu could you briefly explain the key differences between ThreadParker and the corresponding code in libstd?
Oh, so we do not have a guarantee that when the current thread holds a read lock, trying to acquire another read lock is guaranteed to succeed? That could conflict with fairness but it is also extremely useful – actually it seems to be it is basically a requirement to make reentrant read locking not a hazard.
But I see this is a method on RwLock (I somehow expected it would be on RwLockReadGuard), so I could get my guarantee by always using read_recursive. I just have to know I have to do that. (Also, once we come to API bikeshedding I’d suggest to call it read_reentrant. This is not just about recursive functions.)