@@ -23,13 +23,6 @@ use super::qualifs::{self, HasMutInterior, NeedsDrop};
2323use super :: resolver:: FlowSensitiveAnalysis ;
2424use super :: { ConstKind , Item , Qualif , is_lang_panic_fn} ;
2525
26- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
27- pub enum CheckOpResult {
28- Forbidden ,
29- Unleashed ,
30- Allowed ,
31- }
32-
3326pub type IndirectlyMutableResults < ' mir , ' tcx > =
3427 old_dataflow:: DataflowResultsCursor < ' mir , ' tcx , IndirectlyMutableLocals < ' mir , ' tcx > > ;
3528
@@ -149,17 +142,6 @@ pub struct Validator<'a, 'mir, 'tcx> {
149142
150143 /// The span of the current statement.
151144 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 > ,
163145}
164146
165147impl Deref for Validator < ' _ , ' mir , ' tcx > {
@@ -213,7 +195,6 @@ impl Validator<'a, 'mir, 'tcx> {
213195 span : item. body . span ,
214196 item,
215197 qualifs,
216- derived_from_illegal_borrow : BitSet :: new_empty ( item. body . local_decls . len ( ) ) ,
217198 }
218199 }
219200
@@ -258,15 +239,15 @@ impl Validator<'a, 'mir, 'tcx> {
258239 }
259240
260241 /// 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 )
263244 where
264245 O : NonConstOp
265246 {
266247 trace ! ( "check_op: op={:?}" , op) ;
267248
268249 if op. is_allowed_in_item ( self ) {
269- return CheckOpResult :: Allowed ;
250+ return ;
270251 }
271252
272253 // 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> {
276257
277258 if is_unleashable && self . tcx . sess . opts . debugging_opts . unleash_the_miri_inside_of_you {
278259 self . tcx . sess . span_warn ( span, "skipping const checks" ) ;
279- return CheckOpResult :: Unleashed ;
260+ return ;
280261 }
281262
282263 op. emit_error ( self , span) ;
283- CheckOpResult :: Forbidden
284264 }
285265
286266 /// 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 ) {
288268 let span = self . span ;
289269 self . check_op_spanned ( op, span)
290270 }
291271
292- fn check_static ( & mut self , def_id : DefId , span : Span ) -> CheckOpResult {
272+ fn check_static ( & mut self , def_id : DefId , span : Span ) {
293273 let is_thread_local = self . tcx . has_attr ( def_id, sym:: thread_local) ;
294274 if is_thread_local {
295275 self . check_op_spanned ( ops:: ThreadLocalAccess , span)
@@ -322,20 +302,9 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
322302 fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
323303 trace ! ( "visit_rvalue: rvalue={:?} location={:?}" , rvalue, location) ;
324304
325- // Check nested operands and places .
305+ // Special-case reborrows to be more like a copy of a reference .
326306 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) {
339308 let ctx = match kind {
340309 BorrowKind :: Shared => PlaceContext :: NonMutatingUse (
341310 NonMutatingUseContext :: SharedBorrow ,
@@ -351,14 +320,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
351320 ) ,
352321 } ;
353322 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 ;
357325 }
358- } else {
359- self . super_rvalue ( rvalue, location) ;
360326 }
361327
328+ self . super_rvalue ( rvalue, location) ;
329+
362330 match * rvalue {
363331 Rvalue :: Use ( _) |
364332 Rvalue :: Repeat ( ..) |
@@ -369,9 +337,58 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
369337 Rvalue :: Cast ( CastKind :: Pointer ( _) , ..) |
370338 Rvalue :: Discriminant ( ..) |
371339 Rvalue :: Len ( _) |
372- Rvalue :: Ref ( ..) |
373340 Rvalue :: Aggregate ( ..) => { }
374341
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+
375392 Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) => {
376393 let operand_ty = operand. ty ( self . body , self . tcx ) ;
377394 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> {
436453 }
437454 }
438455
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-
491456 fn visit_projection_elem (
492457 & mut self ,
493458 place_base : & PlaceBase < ' tcx > ,
@@ -724,3 +689,36 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId)
724689 }
725690 } ) ;
726691}
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