3 ms·
This is rather fascinating to me, as someone who maintains a flow-sensitive checker. No relation to the Rust borrow checker, since mine is an unsound linter for
by sfink 3y ago
This is rather fascinating to me, as someone who maintains a flow-sensitive checker. No relation to the Rust borrow checker, since mine is an unsound linter for C++ code. Though there are similarities, as mine is also about memory safety (it looks for pointers that are live across a function call that might GC, and therefore could invalidate them by moving the data they're pointing to.) I spend a lot of effort trying to make the errors comprehensible, so I have to think about the mental models people are using.
The entry point for people is almost always a reported error. The notion "live across a GC call" isn't natural to most people, so I dump out the entire live range (which might be from the previous iteration of a loop, for example) in the hopes that it'll sort of tell a story: "see, you set the variable's value to a pointer here, then you run this code, and this function call within it might GC, and then you use the variable's value here". But that's just an example path through the function, and potentially confusing if that particular path is not possible in practice (but another unmentioned path is). That's the risk of having a more precise analysis: there are more ways that the specific error can be wrong even though the report is a true positive.
Earlier, the analysis was simpler to mentally model: is there a path fragment that mentions variable X, then calls something that can GC, and finally mentions X again? But since then, I've added much more precise data flow to check whether the final use of X is an actual use and not clobbering the previous value (in which case, its live range ended sometime earlier). As with the borrow checker, that just provides reasons to not report an error that the simpler analysis would have, but those do matter to the user. Especially when they're struggling to restructure code, and having to mentally do the same analysis to figure out what changes will fix the problem.
In my experience, there are usually simple code fixes that eliminate a problem entirely and would have worked with the simpler analysis. For the more complicated cases, users are generally pretty appreciative when the analysis is smart enough to allow fixes to be more targeted and natural. (They aren't appreciative of the false positives that the more sophisticated analysis never showed them, because they pretty much only think of this stuff when they get an error report. But that's ok!)
- steveklabnik 3y agoThat sounds like an interesting project! I wonder if the soundness is part of it, see nicoburns' sibling comment. But error messages are quite hard. If I recall correctly, in Rust's case, error messages got better with NLL, but that may have been because a tremendous amount of work was put into it by people who were concerned that it was going to become harder to understand, rather than some property of NLL itself. It certainly was an active concern at the time, though the exact timelines and steps are a bit fuzzy to me these days.