@@ -23,13 +23,6 @@ use super::qualifs::{self, HasMutInterior, NeedsDrop};
23
23
use super :: resolver:: FlowSensitiveAnalysis ;
24
24
use super :: { ConstKind , Item , Qualif , is_lang_panic_fn} ;
25
25
26
- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
27
- pub enum CheckOpResult {
28
- Forbidden ,
29
- Unleashed ,
30
- Allowed ,
31
- }
32
-
33
26
pub type IndirectlyMutableResults < ' mir , ' tcx > =
34
27
old_dataflow:: DataflowResultsCursor < ' mir , ' tcx , IndirectlyMutableLocals < ' mir , ' tcx > > ;
35
28
@@ -149,17 +142,6 @@ pub struct Validator<'a, 'mir, 'tcx> {
149
142
150
143
/// The span of the current statement.
151
144
span : Span ,
152
-
153
- /// True if the local was assigned the result of an illegal borrow (`ops::MutBorrow`).
154
- ///
155
- /// This is used to hide errors from {re,}borrowing the newly-assigned local, instead pointing
156
- /// the user to the place where the illegal borrow occurred. This set is only populated once an
157
- /// error has been emitted, so it will never cause an erroneous `mir::Body` to pass validation.
158
- ///
159
- /// FIXME(ecstaticmorse): assert at the end of checking that if `tcx.has_errors() == false`,
160
- /// this set is empty. Note that if we start removing locals from
161
- /// `derived_from_illegal_borrow`, just checking at the end won't be enough.
162
- derived_from_illegal_borrow : BitSet < Local > ,
163
145
}
164
146
165
147
impl Deref for Validator < ' _ , ' mir , ' tcx > {
@@ -213,7 +195,6 @@ impl Validator<'a, 'mir, 'tcx> {
213
195
span : item. body . span ,
214
196
item,
215
197
qualifs,
216
- derived_from_illegal_borrow : BitSet :: new_empty ( item. body . local_decls . len ( ) ) ,
217
198
}
218
199
}
219
200
@@ -258,15 +239,15 @@ impl Validator<'a, 'mir, 'tcx> {
258
239
}
259
240
260
241
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
261
- /// context. Returns `Forbidden` if an error was emitted.
262
- pub fn check_op_spanned < O > ( & mut self , op : O , span : Span ) -> CheckOpResult
242
+ /// context.
243
+ pub fn check_op_spanned < O > ( & mut self , op : O , span : Span )
263
244
where
264
245
O : NonConstOp
265
246
{
266
247
trace ! ( "check_op: op={:?}" , op) ;
267
248
268
249
if op. is_allowed_in_item ( self ) {
269
- return CheckOpResult :: Allowed ;
250
+ return ;
270
251
}
271
252
272
253
// If an operation is supported in miri (and is not already controlled by a feature gate) it
@@ -276,20 +257,19 @@ impl Validator<'a, 'mir, 'tcx> {
276
257
277
258
if is_unleashable && self . tcx . sess . opts . debugging_opts . unleash_the_miri_inside_of_you {
278
259
self . tcx . sess . span_warn ( span, "skipping const checks" ) ;
279
- return CheckOpResult :: Unleashed ;
260
+ return ;
280
261
}
281
262
282
263
op. emit_error ( self , span) ;
283
- CheckOpResult :: Forbidden
284
264
}
285
265
286
266
/// Emits an error if an expression cannot be evaluated in the current context.
287
- pub fn check_op ( & mut self , op : impl NonConstOp ) -> CheckOpResult {
267
+ pub fn check_op ( & mut self , op : impl NonConstOp ) {
288
268
let span = self . span ;
289
269
self . check_op_spanned ( op, span)
290
270
}
291
271
292
- fn check_static ( & mut self , def_id : DefId , span : Span ) -> CheckOpResult {
272
+ fn check_static ( & mut self , def_id : DefId , span : Span ) {
293
273
let is_thread_local = self . tcx . has_attr ( def_id, sym:: thread_local) ;
294
274
if is_thread_local {
295
275
self . check_op_spanned ( ops:: ThreadLocalAccess , span)
@@ -322,20 +302,9 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
322
302
fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
323
303
trace ! ( "visit_rvalue: rvalue={:?} location={:?}" , rvalue, location) ;
324
304
325
- // Check nested operands and places .
305
+ // Special-case reborrows to be more like a copy of a reference .
326
306
if let Rvalue :: Ref ( _, kind, ref place) = * rvalue {
327
- // Special-case reborrows to be more like a copy of a reference.
328
- let mut reborrow_place = None ;
329
- if let & [ ref proj_base @ .., elem] = place. projection . as_ref ( ) {
330
- if elem == ProjectionElem :: Deref {
331
- let base_ty = Place :: ty_from ( & place. base , proj_base, self . body , self . tcx ) . ty ;
332
- if let ty:: Ref ( ..) = base_ty. kind {
333
- reborrow_place = Some ( proj_base) ;
334
- }
335
- }
336
- }
337
-
338
- if let Some ( proj) = reborrow_place {
307
+ if let Some ( reborrowed_proj) = place_as_reborrow ( self . tcx , self . body , place) {
339
308
let ctx = match kind {
340
309
BorrowKind :: Shared => PlaceContext :: NonMutatingUse (
341
310
NonMutatingUseContext :: SharedBorrow ,
@@ -351,14 +320,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
351
320
) ,
352
321
} ;
353
322
self . visit_place_base ( & place. base , ctx, location) ;
354
- self . visit_projection ( & place. base , proj, ctx, location) ;
355
- } else {
356
- self . super_rvalue ( rvalue, location) ;
323
+ self . visit_projection ( & place. base , reborrowed_proj, ctx, location) ;
324
+ return ;
357
325
}
358
- } else {
359
- self . super_rvalue ( rvalue, location) ;
360
326
}
361
327
328
+ self . super_rvalue ( rvalue, location) ;
329
+
362
330
match * rvalue {
363
331
Rvalue :: Use ( _) |
364
332
Rvalue :: Repeat ( ..) |
@@ -369,9 +337,58 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
369
337
Rvalue :: Cast ( CastKind :: Pointer ( _) , ..) |
370
338
Rvalue :: Discriminant ( ..) |
371
339
Rvalue :: Len ( _) |
372
- Rvalue :: Ref ( ..) |
373
340
Rvalue :: Aggregate ( ..) => { }
374
341
342
+ | Rvalue :: Ref ( _, kind @ BorrowKind :: Mut { .. } , ref place)
343
+ | Rvalue :: Ref ( _, kind @ BorrowKind :: Unique , ref place)
344
+ => {
345
+ let ty = place. ty ( self . body , self . tcx ) . ty ;
346
+ let is_allowed = match ty. kind {
347
+ // Inside a `static mut`, `&mut [...]` is allowed.
348
+ ty:: Array ( ..) | ty:: Slice ( _) if self . const_kind ( ) == ConstKind :: StaticMut
349
+ => true ,
350
+
351
+ // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
352
+ // that this is merely a ZST and it is already eligible for promotion.
353
+ // This may require an RFC?
354
+ /*
355
+ ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
356
+ => true,
357
+ */
358
+
359
+ _ => false ,
360
+ } ;
361
+
362
+ if !is_allowed {
363
+ self . check_op ( ops:: MutBorrow ( kind) ) ;
364
+ }
365
+ }
366
+
367
+ // At the moment, `PlaceBase::Static` is only used for promoted MIR.
368
+ | Rvalue :: Ref ( _, BorrowKind :: Shared , ref place)
369
+ | Rvalue :: Ref ( _, BorrowKind :: Shallow , ref place)
370
+ if matches ! ( place. base, PlaceBase :: Static ( _) )
371
+ => bug ! ( "Saw a promoted during const-checking, which must run before promotion" ) ,
372
+
373
+ | Rvalue :: Ref ( _, kind @ BorrowKind :: Shared , ref place)
374
+ | Rvalue :: Ref ( _, kind @ BorrowKind :: Shallow , ref place)
375
+ => {
376
+ // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually
377
+ // seek the cursors beforehand.
378
+ self . qualifs . has_mut_interior . cursor . seek_before ( location) ;
379
+ self . qualifs . indirectly_mutable . seek ( location) ;
380
+
381
+ let borrowed_place_has_mut_interior = HasMutInterior :: in_place (
382
+ & self . item ,
383
+ & |local| self . qualifs . has_mut_interior_eager_seek ( local) ,
384
+ place. as_ref ( ) ,
385
+ ) ;
386
+
387
+ if borrowed_place_has_mut_interior {
388
+ self . check_op ( ops:: MutBorrow ( kind) ) ;
389
+ }
390
+ }
391
+
375
392
Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) => {
376
393
let operand_ty = operand. ty ( self . body , self . tcx ) ;
377
394
let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
@@ -436,58 +453,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
436
453
}
437
454
}
438
455
439
- fn visit_assign ( & mut self , dest : & Place < ' tcx > , rvalue : & Rvalue < ' tcx > , location : Location ) {
440
- trace ! ( "visit_assign: dest={:?} rvalue={:?} location={:?}" , dest, rvalue, location) ;
441
-
442
- // Error on mutable borrows or shared borrows of values with interior mutability.
443
- //
444
- // This replicates the logic at the start of `assign` in the old const checker. Note that
445
- // it depends on `HasMutInterior` being set for mutable borrows as well as values with
446
- // interior mutability.
447
- if let Rvalue :: Ref ( _, kind, ref borrowed_place) = * rvalue {
448
- // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually seek
449
- // the cursors beforehand.
450
- self . qualifs . has_mut_interior . cursor . seek_before ( location) ;
451
- self . qualifs . indirectly_mutable . seek ( location) ;
452
-
453
- let rvalue_has_mut_interior = HasMutInterior :: in_rvalue (
454
- & self . item ,
455
- & |local| self . qualifs . has_mut_interior_eager_seek ( local) ,
456
- rvalue,
457
- ) ;
458
-
459
- if rvalue_has_mut_interior {
460
- let is_derived_from_illegal_borrow = match borrowed_place. as_local ( ) {
461
- // If an unprojected local was borrowed and its value was the result of an
462
- // illegal borrow, suppress this error and mark the result of this borrow as
463
- // illegal as well.
464
- Some ( borrowed_local)
465
- if self . derived_from_illegal_borrow . contains ( borrowed_local) =>
466
- {
467
- true
468
- }
469
-
470
- // Otherwise proceed normally: check the legality of a mutable borrow in this
471
- // context.
472
- _ => self . check_op ( ops:: MutBorrow ( kind) ) == CheckOpResult :: Forbidden ,
473
- } ;
474
-
475
- // When the target of the assignment is a local with no projections, mark it as
476
- // derived from an illegal borrow if necessary.
477
- //
478
- // FIXME: should we also clear `derived_from_illegal_borrow` when a local is
479
- // assigned a new value?
480
- if is_derived_from_illegal_borrow {
481
- if let Some ( dest) = dest. as_local ( ) {
482
- self . derived_from_illegal_borrow . insert ( dest) ;
483
- }
484
- }
485
- }
486
- }
487
-
488
- self . super_assign ( dest, rvalue, location) ;
489
- }
490
-
491
456
fn visit_projection_elem (
492
457
& mut self ,
493
458
place_base : & PlaceBase < ' tcx > ,
@@ -724,3 +689,36 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId)
724
689
}
725
690
} ) ;
726
691
}
692
+
693
+ fn place_as_reborrow (
694
+ tcx : TyCtxt < ' tcx > ,
695
+ body : & Body < ' tcx > ,
696
+ place : & ' a Place < ' tcx > ,
697
+ ) -> Option < & ' a [ PlaceElem < ' tcx > ] > {
698
+ place
699
+ . projection
700
+ . split_last ( )
701
+ . and_then ( |( outermost, inner) | {
702
+ if outermost != & ProjectionElem :: Deref {
703
+ return None ;
704
+ }
705
+
706
+ // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
707
+ // that points to the allocation for the static. Don't treat these as reborrows.
708
+ if let PlaceBase :: Local ( local) = place. base {
709
+ if body. local_decls [ local] . is_ref_to_static ( ) {
710
+ return None ;
711
+ }
712
+ }
713
+
714
+ // Ensure the type being derefed is a reference and not a raw pointer.
715
+ //
716
+ // This is sufficient to prevent an access to a `static mut` from being marked as a
717
+ // reborrow, even if the check above were to disappear.
718
+ let inner_ty = Place :: ty_from ( & place. base , inner, body, tcx) . ty ;
719
+ match inner_ty. kind {
720
+ ty:: Ref ( ..) => Some ( inner) ,
721
+ _ => None ,
722
+ }
723
+ } )
724
+ }
0 commit comments