@@ -12,13 +12,13 @@ pub(crate) struct PointerCheck<'tcx> {
12
12
pub(crate) assert_kind: Box<AssertKind<Operand<'tcx>>>,
13
13
}
14
14
15
- /// Indicates whether we insert the checks for borrow places of a raw pointer.
16
- /// Concretely places with [MutatingUseContext::Borrow] or
17
- /// [NonMutatingUseContext::SharedBorrow] .
15
+ /// When checking for borrows of field projections (`&(*ptr).a`), we might want
16
+ /// to check for the field type (type of `.a` in the example). This enum defines
17
+ /// the variations (pass the pointer [Ty] or the field [Ty]) .
18
18
#[derive(Copy, Clone)]
19
- pub(crate) enum BorrowCheckMode {
20
- IncludeBorrows ,
21
- ExcludeBorrows ,
19
+ pub(crate) enum BorrowedFieldProjectionMode {
20
+ FollowProjections ,
21
+ NoFollowProjections ,
22
22
}
23
23
24
24
/// Utility for adding a check for read/write on every sized, raw pointer.
@@ -27,8 +27,8 @@ pub(crate) enum BorrowCheckMode {
27
27
/// new basic block directly before the pointer access. (Read/write accesses
28
28
/// are determined by the `PlaceContext` of the MIR visitor.) Then calls
29
29
/// `on_finding` to insert the actual logic for a pointer check (e.g. check for
30
- /// alignment). A check can choose to be inserted for (mutable) borrows of
31
- /// raw pointers via the `borrow_check_mode ` parameter.
30
+ /// alignment). A check can choose to follow borrows of field projections via
31
+ /// the `field_projection_mode ` parameter.
32
32
///
33
33
/// This utility takes care of the right order of blocks, the only thing a
34
34
/// caller must do in `on_finding` is:
@@ -45,7 +45,7 @@ pub(crate) fn check_pointers<'tcx, F>(
45
45
body: &mut Body<'tcx>,
46
46
excluded_pointees: &[Ty<'tcx>],
47
47
on_finding: F,
48
- borrow_check_mode: BorrowCheckMode ,
48
+ field_projection_mode: BorrowedFieldProjectionMode ,
49
49
) where
50
50
F: Fn(
51
51
/* tcx: */ TyCtxt<'tcx>,
@@ -82,7 +82,7 @@ pub(crate) fn check_pointers<'tcx, F>(
82
82
local_decls,
83
83
typing_env,
84
84
excluded_pointees,
85
- borrow_check_mode ,
85
+ field_projection_mode ,
86
86
);
87
87
finder.visit_statement(statement, location);
88
88
@@ -128,7 +128,7 @@ struct PointerFinder<'a, 'tcx> {
128
128
typing_env: ty::TypingEnv<'tcx>,
129
129
pointers: Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)>,
130
130
excluded_pointees: &'a [Ty<'tcx>],
131
- borrow_check_mode: BorrowCheckMode ,
131
+ field_projection_mode: BorrowedFieldProjectionMode ,
132
132
}
133
133
134
134
impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
@@ -137,15 +137,15 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
137
137
local_decls: &'a mut LocalDecls<'tcx>,
138
138
typing_env: ty::TypingEnv<'tcx>,
139
139
excluded_pointees: &'a [Ty<'tcx>],
140
- borrow_check_mode: BorrowCheckMode ,
140
+ field_projection_mode: BorrowedFieldProjectionMode ,
141
141
) -> Self {
142
142
PointerFinder {
143
143
tcx,
144
144
local_decls,
145
145
typing_env,
146
146
excluded_pointees,
147
147
pointers: Vec::new(),
148
- borrow_check_mode ,
148
+ field_projection_mode ,
149
149
}
150
150
}
151
151
@@ -163,15 +163,14 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
163
163
MutatingUseContext::Store
164
164
| MutatingUseContext::Call
165
165
| MutatingUseContext::Yield
166
- | MutatingUseContext::Drop,
166
+ | MutatingUseContext::Drop
167
+ | MutatingUseContext::Borrow,
167
168
) => true,
168
169
PlaceContext::NonMutatingUse(
169
- NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
170
+ NonMutatingUseContext::Copy
171
+ | NonMutatingUseContext::Move
172
+ | NonMutatingUseContext::SharedBorrow,
170
173
) => true,
171
- PlaceContext::MutatingUse(MutatingUseContext::Borrow)
172
- | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
173
- matches!(self.borrow_check_mode, BorrowCheckMode::IncludeBorrows)
174
- }
175
174
_ => false,
176
175
}
177
176
}
@@ -183,19 +182,29 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
183
182
return;
184
183
}
185
184
186
- // Since Deref projections must come first and only once, the pointer for an indirect place
187
- // is the Local that the Place is based on.
185
+ // Get the place and type we visit.
188
186
let pointer = Place::from(place.local);
189
- let pointer_ty = self.local_decls[place.local] .ty;
187
+ let pointer_ty = pointer.ty( self.local_decls, self.tcx) .ty;
190
188
191
189
// We only want to check places based on raw pointers
192
- if ! pointer_ty.is_raw_ptr() {
190
+ let &ty::RawPtr(mut pointee_ty, _) = pointer_ty.kind() else {
193
191
trace!("Indirect, but not based on an raw ptr, not checking {:?}", place);
194
192
return;
193
+ };
194
+
195
+ // If we see a borrow of a field projection, we want to pass the field type to the
196
+ // check and not the pointee type.
197
+ if matches!(self.field_projection_mode, BorrowedFieldProjectionMode::FollowProjections)
198
+ && matches!(
199
+ context,
200
+ PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
201
+ | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
202
+ )
203
+ {
204
+ // Naturally, the field type is type of the initial place we look at.
205
+ pointee_ty = place.ty(self.local_decls, self.tcx).ty;
195
206
}
196
207
197
- let pointee_ty =
198
- pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer");
199
208
// Ideally we'd support this in the future, but for now we are limited to sized types.
200
209
if !pointee_ty.is_sized(self.tcx, self.typing_env) {
201
210
trace!("Raw pointer, but pointee is not known to be sized: {:?}", pointer_ty);
@@ -207,6 +216,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
207
216
ty::Array(ty, _) => *ty,
208
217
_ => pointee_ty,
209
218
};
219
+ // Check if we excluded this pointee type from the check.
210
220
if self.excluded_pointees.contains(&element_ty) {
211
221
trace!("Skipping pointer for type: {:?}", pointee_ty);
212
222
return;
0 commit comments