Skip to content

unsafe_code lint fires for unsafe fn with safe body #108926

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
chbaker0 opened this issue Mar 9, 2023 · 6 comments
Open

unsafe_code lint fires for unsafe fn with safe body #108926

chbaker0 opened this issue Mar 9, 2023 · 6 comments
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@chbaker0
Copy link
Contributor

chbaker0 commented Mar 9, 2023

#![forbid(unsafe_op_in_unsafe_fn)]
#![forbid(unsafe_code)]

trait Foo {
    unsafe fn dangerous();
}

struct SafeImpl;

impl Foo for SafeImpl {
    unsafe fn dangerous() {
        println!("this is safe!");
    }
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=3f3fbdb3494c6770487823f284c303f9

With forbid(unsafe_op_in_unsafe_fn), an unsafe fn's body in principle no longer has an implicit unsafe block. So, unsafe_code should not trigger on an unsafe fn alone: there is not necessarily any unsafe code inside!

This has practical effect. Consider one crate defining interfaces with unsafe methods (e.g. representing an OS API). Another crate wants to create mock implementations of these interfaces that are 100% safe. While any crate consuming these mocks will necessarily contain unsafe {...} blocks, it is nice for the test support crate to declare it itself has only safe code.

@chbaker0
Copy link
Contributor Author

chbaker0 commented Mar 9, 2023

@rustbot label +A-diagnostics

@rustbot rustbot added the A-diagnostics Area: Messages for errors, warnings, and lints label Mar 9, 2023
@mu001999
Copy link
Contributor

mu001999 commented Mar 9, 2023

What about unsafe traits? They are also forbidden by lint unsafe_code.

@chbaker0
Copy link
Contributor Author

chbaker0 commented Mar 9, 2023

Yes, unsafe trait items should be allowed too IMO, it's only unsafe impl that's actually unsafe.

Both unsafe trait and unsafe fn say "users must follow my contract", while unsafe { ... } blocks and unsafe impl say "I'm following the contracts defined by the interfaces I'm using". Only the latter are asserting things the compiler can't check.

@Ezrashaw
Copy link
Contributor

@rustbot claim

@Noratrieb Noratrieb added T-lang Relevant to the language team, which will review and decide on the PR/issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 5, 2023
@RalfJung
Copy link
Member

I agree, the current behavior is quite confusing (but at least it is consistent between unsafe fn and unsafe trait).

@ia0
Copy link
Contributor

ia0 commented Feb 24, 2024

I agree there seems to be a general confusion regarding unsafe, so I decided to write down my mental model to help education in that regard (the main idea being to make the proof type explicit as it would be in dependent type programming languages). This general confusion could be decomposed in the following core confusions:

  • Confusion between the unsafe keyword in types and in code. In types, the unsafe keyword represents properties (things to prove). In code, the unsafe keyword represents proofs (proving the properties of the unsafe keyword in the associated type). This confusion is accentuated by the fact that it's the same unsafe keyword in both cases (versus say using unsafeprop in types and unsafeproof in code, e.g. unsafeprop trait and unsafeproof impl).
  • Confusion between pre- and post-conditions, or more generally between requirements and guarantees. Requirements are things to prove. Guarantees are things you can rely on to prove requirements. This confusion is accentuated by the fact that there is currently no simple way to have post-conditions (there's only unsafe traits and it's quite a heavy encoding) and by the fact that "pre" and "post" may be interpreted as the code "before" and "after" the function call while it is actually about the properties to provide and receive from the function call. In particular, pre-conditions may refer to code after the function call (like Pin::get_unchecked_mut or String::as_mut_vec) and post-conditions may refer to data before the function call.

I am not recommending Rust to change anything. I am just trying to lift this general confusion regarding unsafe through educational materials. Whether Rust should change anything (and how) is up to the community as a whole. In particular, lifting the confusion should be enough to clarify issues like this one. Once the first core confusion is lifted it should be obvious that unsafe fns and unsafe traits are not unsafe code and should not lint. Only unsafe blocks and unsafe impls should lint.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants