>> On the other hand, it ought to be feasible to implement that kind of >> ’negative reasoning' as a custom lint. It might not work as well as >> something built into the language, but it should work decently well, >> and could serve as a prototype for a future built-in feature. > > Interesting, do you have an example somewhere? > > Regards, > Boqun After some searching, I found this, which someone wrote several years ago for a very similar purpose: https://github.com/thepowersgang/tag_safe/ > This is a linter designed originally for use with a kernel, where functions > need to be marked as "IRQ safe" (meaning they are safe to call within an IRQ > handler, and handle the case where they may interrupt themselves). > If a function is annotated with #[req_safe(ident)] (where ident can be > anything, and defines the type of safety) this linter will check that all > functions called by that function are either annotated with the same > annotation or #[is_safe(ident)], OR they do not call functions with the > reverse #[is_unsafe(ident)] annotation. Note that the code won't work as-is with recent rustc. rustc's API for custom lints is not stable, and in fact rustc has deprecated linter plugins entirely [1], though there are alternative approaches to using custom lints [2]. Still, it's a good example of the approach. One fundamental caveat is that it doesn't seem to have the sophistication needed to be sound with respect to indirect calls. For example, suppose you have a function that fetches a callback from some structure and calls it. Whether this function is IRQ-safe depends on whether the callback is expected to be IRQ-safe, so in order to safety-check this, you would need an annotation on either the callback field or the function pointer type. This is more complex than just putting annotations on function definitions. Or suppose you have the following code: fn foo() { bar(|| do_something_not_irq_safe()); } If `foo` is expected to be IRQ-safe, this may or may not be sound, depending on whether `bar` calls the callback immediately or saves it for later. If `bar` saves it for later, then it could be marked unconditionally IRQ-safe. But if `bar` calls it immediately, then it's neither IRQ-safe nor IRQ-unsafe, but effectively generic over IRQ safety. You could pessimistically mark it IRQ-unsafe, but Rust has tons of basic helper methods that accept callbacks and call them immediately; not being able to use any of them in an IRQ-safe context would be quite limiting. In short, a fully sound approach requires not just checking which functions call which, but having some kind of integration with the type system. This is the kind of issue that I was thinking of when I said a custom lint may not work as well as something built into the language. However, I do think it's *possible* to handle it soundly from a lint, especially if it focuses on typical use cases and relies on manual annotations for the rest. Alternately, even an unsound lint would be a good first step. It wouldn't really comport with Rust's ethos of making safety guarantees ironclad rather than heuristic, but it would serve as a good proof of concept for a future language feature, while likely being helpful in practice in the short term. [1] https://github.com/rust-lang/rust/pull/64675/files [2] https://www.trailofbits.com/post/write-rust-lints-without-forking-clippy