From 4696e845891c85b322ee47e2c921a1faf93622c9 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 5 Jan 2024 14:04:49 +0000 Subject: [PATCH 1/3] Document unsafety checking --- src/SUMMARY.md | 1 + src/thir.md | 9 ++--- src/unsafety-checking.md | 72 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 src/unsafety-checking.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index e484b6af6..345323ead 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -141,6 +141,7 @@ - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md) - [Effect checking](./effects.md) - [Pattern and Exhaustiveness Checking](./pat-exhaustive-checking.md) +- [Unsafety Checking](./unsafety-checking.md) - [MIR dataflow](./mir/dataflow.md) - [Drop elaboration](./mir/drop-elaboration.md) - [The borrow checker](./borrow_check.md) diff --git a/src/thir.md b/src/thir.md index 2197cad71..9c49cb10b 100644 --- a/src/thir.md +++ b/src/thir.md @@ -4,16 +4,13 @@ The THIR ("Typed High-Level Intermediate Representation"), previously called HAIR for "High-Level Abstract IR", is another IR used by rustc that is generated after -[type checking]. It is (as of April 2022) only used for -[MIR construction] and [exhaustiveness checking]. There is also -[an experimental unsafety checker][thir-unsafeck] that operates on the THIR as a replacement for -the current MIR unsafety checker, and can be used instead of the MIR unsafety checker by passing -the `-Z thir-unsafeck` flag to `rustc`. +[type checking]. It is (as of January 2024) used for +[MIR construction], [exhaustiveness checking], and [unsafety checking]. [type checking]: ./type-checking.md [MIR construction]: ./mir/construction.md [exhaustiveness checking]: ./pat-exhaustive-checking.md -[thir-unsafeck]: https://github.com/rust-lang/compiler-team/issues/402 +[unsafety checking]: ./unsafety-checking.md As the name might suggest, the THIR is a lowered version of the [HIR] where all the types have been filled in, which is possible after type checking has completed. diff --git a/src/unsafety-checking.md b/src/unsafety-checking.md new file mode 100644 index 000000000..c6eaf867a --- /dev/null +++ b/src/unsafety-checking.md @@ -0,0 +1,72 @@ +# Unsafety Checking + +Certain expressions in Rust are can violate memory safety and as such need to be +inside an `unsafe` block or function. The compiler will also warn if an unsafe +block is used without any corresponding unsafe operations. + +## Overview + +The unsafety check is located in the [`check_unsafety`] module. It performs a +walk over the [THIR] of a function and all of its closures and inline constants. +It keeps track of the unsafe context: whether it has entered an `unsafe` block. +If an unsafe operation is used outside of an `unsafe` block, then an error is +reported. If an unsafe operation is used in an unsafe block then that block is +marked as used for [the unused_unsafe lint](#the-unused_unsafe-lint). + +Most unsafe operations can be identified by checking the `ExprKind` in THIR and +checking the type of the argument. For example, dereferences of a raw pointer +correspond to `ExprKind::Deref`s with an argument that has a raw pointer type. + +Looking for unsafe Union field accesses is a bit more complex because writing to +a field of a union is safe. The checker tracks when it's visiting the left-hand +side of an assignment expression and allows union fields to directly appear +there, while erroring in all other cases. Union field accesses can also occur +in patterns, so those have to be walked as well. + +The other complicated safety check is for writes to fields of layout constrained +structs (such as [`NonNull`]). These are found by looking for the borrow or +assignment expression and then visiting the subexpression being borrowed or +assigned with a separate visitor. + +[THIR]: ./thir.md +[`check_unsafety`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/check_unsafety/index.html +[`NonNull`]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html + +## The unused_unsafe lint + +The unused_unsafe lint reports `unsafe` blocks that can be removed. The unsafety +checker records whenever it finds an operation that requires unsafe. The lint is +then reported if either: + +- An `unsafe` block contains no unsafe operations +- An `unsafe` block is within another unsafe block, and the outer block + isn't considered unused + +```rust +#![deny(unused_unsafe)] +let y = 0; +let x: *const u8 = core::ptr::addr_of!(y); +unsafe { // lint reported for this block + unsafe { + let z = *x; + } + let safe_expr = 123; +} +unsafe { + unsafe { // lint reported for this block + let z = *x; + } + let unsafe_expr = *x; +} +``` + +## Other checks involving `unsafe` + +[Unsafe traits] require an `unsafe impl` to be implemented, the check for this +is done as part of [coherence]. The `unsafe_code` lint is run as a lint pass on +the ast that searches for unsafe blocks, functions and implementations, as well +as certain unsafe attributes. + +[Unsafe traits]: https://doc.rust-lang.org/reference/items/traits.html#unsafe-traits +[coherence]: /home/matthew/rust/compiler/rustc_hir_analysis/src/coherence/unsafety.rs + From 93efcdda8b0eb6f844fc470962f12de2d3379545 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 18 Jan 2024 13:43:49 +0000 Subject: [PATCH 2/3] Explain why unsafe checking uses THIR --- src/unsafety-checking.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/unsafety-checking.md b/src/unsafety-checking.md index c6eaf867a..2f2eeae61 100644 --- a/src/unsafety-checking.md +++ b/src/unsafety-checking.md @@ -13,6 +13,13 @@ If an unsafe operation is used outside of an `unsafe` block, then an error is reported. If an unsafe operation is used in an unsafe block then that block is marked as used for [the unused_unsafe lint](#the-unused_unsafe-lint). +The unsafety check needs type information so could potentially be done on the +HIR, making use of typeck results, THIR or MIR. THIR is chosen because there are +fewer cases to consider than in HIR, for example unsafe function calls and +unsafe method calls have the same representation in THIR. The check is not done +on MIR because safety checks do not depend on control flow so MIR is not +necessary to use and MIR doesn't have as precise spans for some expressions. + Most unsafe operations can be identified by checking the `ExprKind` in THIR and checking the type of the argument. For example, dereferences of a raw pointer correspond to `ExprKind::Deref`s with an argument that has a raw pointer type. From 295282d66539b5f8bb082af155f5f37098a05b79 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 20 Jan 2024 20:44:38 +0900 Subject: [PATCH 3/3] Update src/unsafety-checking.md --- src/unsafety-checking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unsafety-checking.md b/src/unsafety-checking.md index 2f2eeae61..d68631f4f 100644 --- a/src/unsafety-checking.md +++ b/src/unsafety-checking.md @@ -1,6 +1,6 @@ # Unsafety Checking -Certain expressions in Rust are can violate memory safety and as such need to be +Certain expressions in Rust can violate memory safety and as such need to be inside an `unsafe` block or function. The compiler will also warn if an unsafe block is used without any corresponding unsafe operations.