Skip to content

Commit 4767d5a

Browse files
Document unsafety checking (#1847)
Co-authored-by: Yuki Okushi <[email protected]>
1 parent 1dc1152 commit 4767d5a

File tree

3 files changed

+83
-6
lines changed

3 files changed

+83
-6
lines changed

src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
- [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md)
143143
- [Effect checking](./effects.md)
144144
- [Pattern and Exhaustiveness Checking](./pat-exhaustive-checking.md)
145+
- [Unsafety Checking](./unsafety-checking.md)
145146
- [MIR dataflow](./mir/dataflow.md)
146147
- [Drop elaboration](./mir/drop-elaboration.md)
147148
- [The borrow checker](./borrow_check.md)

src/thir.md

+3-6
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@
44

55
The THIR ("Typed High-Level Intermediate Representation"), previously called HAIR for
66
"High-Level Abstract IR", is another IR used by rustc that is generated after
7-
[type checking]. It is (as of <!-- date-check --> April 2022) only used for
8-
[MIR construction] and [exhaustiveness checking]. There is also
9-
[an experimental unsafety checker][thir-unsafeck] that operates on the THIR as a replacement for
10-
the current MIR unsafety checker, and can be used instead of the MIR unsafety checker by passing
11-
the `-Z thir-unsafeck` flag to `rustc`.
7+
[type checking]. It is (as of <!-- date-check --> January 2024) used for
8+
[MIR construction], [exhaustiveness checking], and [unsafety checking].
129

1310
[type checking]: ./type-checking.md
1411
[MIR construction]: ./mir/construction.md
1512
[exhaustiveness checking]: ./pat-exhaustive-checking.md
16-
[thir-unsafeck]: https://github.com/rust-lang/compiler-team/issues/402
13+
[unsafety checking]: ./unsafety-checking.md
1714

1815
As the name might suggest, the THIR is a lowered version of the [HIR] where all
1916
the types have been filled in, which is possible after type checking has completed.

src/unsafety-checking.md

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Unsafety Checking
2+
3+
Certain expressions in Rust can violate memory safety and as such need to be
4+
inside an `unsafe` block or function. The compiler will also warn if an unsafe
5+
block is used without any corresponding unsafe operations.
6+
7+
## Overview
8+
9+
The unsafety check is located in the [`check_unsafety`] module. It performs a
10+
walk over the [THIR] of a function and all of its closures and inline constants.
11+
It keeps track of the unsafe context: whether it has entered an `unsafe` block.
12+
If an unsafe operation is used outside of an `unsafe` block, then an error is
13+
reported. If an unsafe operation is used in an unsafe block then that block is
14+
marked as used for [the unused_unsafe lint](#the-unused_unsafe-lint).
15+
16+
The unsafety check needs type information so could potentially be done on the
17+
HIR, making use of typeck results, THIR or MIR. THIR is chosen because there are
18+
fewer cases to consider than in HIR, for example unsafe function calls and
19+
unsafe method calls have the same representation in THIR. The check is not done
20+
on MIR because safety checks do not depend on control flow so MIR is not
21+
necessary to use and MIR doesn't have as precise spans for some expressions.
22+
23+
Most unsafe operations can be identified by checking the `ExprKind` in THIR and
24+
checking the type of the argument. For example, dereferences of a raw pointer
25+
correspond to `ExprKind::Deref`s with an argument that has a raw pointer type.
26+
27+
Looking for unsafe Union field accesses is a bit more complex because writing to
28+
a field of a union is safe. The checker tracks when it's visiting the left-hand
29+
side of an assignment expression and allows union fields to directly appear
30+
there, while erroring in all other cases. Union field accesses can also occur
31+
in patterns, so those have to be walked as well.
32+
33+
The other complicated safety check is for writes to fields of layout constrained
34+
structs (such as [`NonNull`]). These are found by looking for the borrow or
35+
assignment expression and then visiting the subexpression being borrowed or
36+
assigned with a separate visitor.
37+
38+
[THIR]: ./thir.md
39+
[`check_unsafety`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/check_unsafety/index.html
40+
[`NonNull`]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html
41+
42+
## The unused_unsafe lint
43+
44+
The unused_unsafe lint reports `unsafe` blocks that can be removed. The unsafety
45+
checker records whenever it finds an operation that requires unsafe. The lint is
46+
then reported if either:
47+
48+
- An `unsafe` block contains no unsafe operations
49+
- An `unsafe` block is within another unsafe block, and the outer block
50+
isn't considered unused
51+
52+
```rust
53+
#![deny(unused_unsafe)]
54+
let y = 0;
55+
let x: *const u8 = core::ptr::addr_of!(y);
56+
unsafe { // lint reported for this block
57+
unsafe {
58+
let z = *x;
59+
}
60+
let safe_expr = 123;
61+
}
62+
unsafe {
63+
unsafe { // lint reported for this block
64+
let z = *x;
65+
}
66+
let unsafe_expr = *x;
67+
}
68+
```
69+
70+
## Other checks involving `unsafe`
71+
72+
[Unsafe traits] require an `unsafe impl` to be implemented, the check for this
73+
is done as part of [coherence]. The `unsafe_code` lint is run as a lint pass on
74+
the ast that searches for unsafe blocks, functions and implementations, as well
75+
as certain unsafe attributes.
76+
77+
[Unsafe traits]: https://doc.rust-lang.org/reference/items/traits.html#unsafe-traits
78+
[coherence]: /home/matthew/rust/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
79+

0 commit comments

Comments
 (0)