rustc浅析: Unsafety Checking
Dec 15, 2024
这一步检查的实现位于check_unsafety.rs。
引入
Rust中,所有无法由类型系统证明正确性的代码(如裸指针的解引用),以及被unsafe
修饰的traits
,functions
和methods
,都被视为是unsafe的。这一部分的操作需要被放入unsafe
块内。Unsafety Checking确保unsafe操作不被用于unsafe
块之外。
THIR
-----------
Source Code
-----------
|
-----------
Rust AST
-----------
|
-----------
HIR
-----------
|
-----------
THIR
-----------
|
-----------
MIR <-- Control Flow Graph
-----------
Unsafety Checking基于THIR,理由如下:
- 和HIR相比,使用THIR需要考虑的情况更少。比如
unsafe function calls
和unsafe method calls
在THIR中的表示是相同的。 - 不用MIR是因为Unsafety Checking不需要控制流,并且对于一些表达式,MIR没有像THIR那么精确的span。
辨认unsafe操作
绝大部分的unsafe操作可以通过检查THIR中的ExprKind
和检查参数类型来辨认。比如说想要辨认裸指针解引用的操作,可以通过检查具有裸指针参数的 ExprKind::Deref
:
UnsafeVisitor::visit_expr()
:
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_build/src/check_unsafety.rs#L538-L553
=> Deref
Access fields of a union
考虑以下代码:
union MyUnion
- 会发现,写入
u.f1
是safe的,而读取u.f1
是unsafe的。
因此检查器会允许union fields直接出现在赋值表达式的LHS,而其它情况全部报错。
同时读取union fields的操作也会发生在模式匹配,因此那里也需要被检查器走一遍:
UnsafeVisitor::visit_expr()
:
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_build/src/check_unsafety.rs#L623-L643
=> Field
UnsafeVisitor::visit_pat()
:
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_build/src/check_unsafety.rs#L341-L366
match &pat.kind
Anonymous Constant
HIR用AnonConst
表示 anonymous constant(匿名常量)。
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_hir/src/hir.rs#L1682-L1696
/// A constant (expression) that's not an item or associated item,
/// but needs its own `DefId` for type-checking, const-eval, etc.
/// These are usually found nested inside types (e.g., array lengths)
/// or expressions (e.g., repeat counts), and also used to define
/// explicit discriminant values for enum variants.
///
/// You can check if this anon const is a default in a const param
/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_def_id(..)`
- 这里表明到,anonymous constant 并不是一个item。
但实际上,AnonConst
在许多地方会被当作成一个item来处理,比如本文讲到的Unsafety Checking。anonymous constant并不具备继承unsafety的能力,考虑以下代码:
const unsafe
有如下报错信息:
Compiling playground v0.0.1 (/playground)
warning: unnecessary `unsafe` block
--> src/main.rs:4:5
|
4 | unsafe {
| ^^^^^^ unnecessary `unsafe` block
|
= note: `#[warn(unused_unsafe)]` on by default
error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block
--> src/main.rs:5:22
|
4 | unsafe {
| ------ items do not inherit unsafety from separate enclosing items
5 | let _x = [0; foo()];
| ^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
For more information about this error, try `rustc --explain E0133`.
warning: `playground` (bin "playground") generated 1 warning
error: could not compile `playground` (bin "playground") due to 1 previous error; 1 warning emitted
因为这里foo()
被表示为anonymous constant,而anonymous constant在Unsafety Checking中被当作成一个item来处理,因此其不具备继承unsafety的能力,因此报错。