I think we see the same problem but want to solve it in different ways. I want to make the unsafe boundary to be the scope of the unsafe block while you want it to be the module. However most of your points seem to be in favour of my approach.
…people who don’t know what they’re doing may very well be lured into believing that if they don’t modify code that is directly in an unsafe block then they can’t cause any unsafety
This is my point. I want to constrain the unsafey to as small a point as possible.
but this is provably untrue if they’re modifying a module that contains an unsafe block elsewhere.
I don’t think that this is true at all. Try to prove that by adding an unsafe {} to a module will make modifying other areas unsafe.
I see that there are cases where this can be true but I think the correct approach is to attack these areas rather then giving up and allowing off screen code to affect what is being worked on right now.
I would also argue that if you ensure that all invariants hold whenever you exit an unsafe block then you won’t be able to do unsafe things outside of unsafe blocks.
The only hairy bit is calling safe code from unsafe blocks. But I don’t think that is solved by either proposal. However since the root of the problem is in an unsafe block I don’t think that is the worst problem to have. But to get back on topic…
I think that restraining unsafety to unsafe blocks is not a lost cause. While ensuring that this happens can’t be verified by the compiler I think that it fits perfectly with the “you better know what you are doing” nature of unsafe blocks.
Furthermore keeping this boundary ensures that people not working inside unsafe don’t need to concern themselves with these problems even without searching the entire module for the unsafe keyword. I also think that this encourages keeping unsafe blocks to small regions of unsafety that need to be understood as a whole, not often requiring tying that together with nearby unsafe regions.