@@ -11,7 +11,7 @@ use rustc_middle::mir::*;
1111use rustc_middle:: thir:: * ;
1212use rustc_middle:: ty:: { self , AdtDef , CanonicalUserTypeAnnotation , Ty , Variance } ;
1313use rustc_middle:: { bug, span_bug} ;
14- use rustc_span:: Span ;
14+ use rustc_span:: { DesugaringKind , Span } ;
1515use tracing:: { debug, instrument, trace} ;
1616
1717use crate :: builder:: ForGuard :: { OutsideGuard , RefWithinGuard } ;
@@ -630,6 +630,80 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
630630 block. and ( base_place. index ( idx) )
631631 }
632632
633+ /// Given a place that's either an array or a slice, returns an operand
634+ /// with the length of the array/slice.
635+ ///
636+ /// For arrays it'll be `Operand::Constant` with the actual length;
637+ /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`.
638+ fn len_of_slice_or_array (
639+ & mut self ,
640+ block : BasicBlock ,
641+ place : Place < ' tcx > ,
642+ span : Span ,
643+ source_info : SourceInfo ,
644+ ) -> Operand < ' tcx > {
645+ let place_ty = place. ty ( & self . local_decls , self . tcx ) . ty ;
646+ let usize_ty = self . tcx . types . usize ;
647+
648+ match place_ty. kind ( ) {
649+ ty:: Array ( _elem_ty, len_const) => {
650+ // We know how long an array is, so just use that as a constant
651+ // directly -- no locals needed. We do need one statement so
652+ // that borrow- and initialization-checking consider it used,
653+ // though. FIXME: Do we really *need* to count this as a use?
654+ // Could partial array tracking work off something else instead?
655+ self . cfg . push_fake_read ( block, source_info, FakeReadCause :: ForIndex , place) ;
656+ let const_ = Const :: from_ty_const ( * len_const, usize_ty, self . tcx ) ;
657+ Operand :: Constant ( Box :: new ( ConstOperand { span, user_ty : None , const_ } ) )
658+ }
659+ ty:: Slice ( _elem_ty) => {
660+ let ptr_or_ref = if let [ PlaceElem :: Deref ] = place. projection [ ..]
661+ && let local_ty = self . local_decls [ place. local ] . ty
662+ && local_ty. is_trivially_pure_clone_copy ( )
663+ {
664+ // It's extremely common that we have something that can be
665+ // directly passed to `PtrMetadata`, so avoid an unnecessary
666+ // temporary and statement in those cases. Note that we can
667+ // only do that for `Copy` types -- not `&mut [_]` -- because
668+ // the MIR we're building here needs to pass NLL later.
669+ Operand :: Copy ( Place :: from ( place. local ) )
670+ } else {
671+ let len_span = self . tcx . with_stable_hashing_context ( |hcx| {
672+ let span = source_info. span ;
673+ span. mark_with_reason (
674+ None ,
675+ DesugaringKind :: IndexBoundsCheckReborrow ,
676+ span. edition ( ) ,
677+ hcx,
678+ )
679+ } ) ;
680+ let ptr_ty = Ty :: new_imm_ptr ( self . tcx , place_ty) ;
681+ let slice_ptr = self . temp ( ptr_ty, span) ;
682+ self . cfg . push_assign (
683+ block,
684+ SourceInfo { span : len_span, ..source_info } ,
685+ slice_ptr,
686+ Rvalue :: RawPtr ( Mutability :: Not , place) ,
687+ ) ;
688+ Operand :: Move ( slice_ptr)
689+ } ;
690+
691+ let len = self . temp ( usize_ty, span) ;
692+ self . cfg . push_assign (
693+ block,
694+ source_info,
695+ len,
696+ Rvalue :: UnaryOp ( UnOp :: PtrMetadata , ptr_or_ref) ,
697+ ) ;
698+
699+ Operand :: Move ( len)
700+ }
701+ _ => {
702+ span_bug ! ( span, "len called on place of type {place_ty:?}" )
703+ }
704+ }
705+ }
706+
633707 fn bounds_check (
634708 & mut self ,
635709 block : BasicBlock ,
@@ -638,25 +712,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
638712 expr_span : Span ,
639713 source_info : SourceInfo ,
640714 ) -> BasicBlock {
641- let usize_ty = self . tcx . types . usize ;
642- let bool_ty = self . tcx . types . bool ;
643- // bounds check:
644- let len = self . temp ( usize_ty, expr_span) ;
645- let lt = self . temp ( bool_ty, expr_span) ;
715+ let slice = slice. to_place ( self ) ;
646716
647717 // len = len(slice)
648- self . cfg . push_assign ( block, source_info, len, Rvalue :: Len ( slice. to_place ( self ) ) ) ;
718+ let len = self . len_of_slice_or_array ( block, slice, expr_span, source_info) ;
719+
649720 // lt = idx < len
721+ let bool_ty = self . tcx . types . bool ;
722+ let lt = self . temp ( bool_ty, expr_span) ;
650723 self . cfg . push_assign (
651724 block,
652725 source_info,
653726 lt,
654727 Rvalue :: BinaryOp (
655728 BinOp :: Lt ,
656- Box :: new ( ( Operand :: Copy ( Place :: from ( index) ) , Operand :: Copy ( len) ) ) ,
729+ Box :: new ( ( Operand :: Copy ( Place :: from ( index) ) , len. to_copy ( ) ) ) ,
657730 ) ,
658731 ) ;
659- let msg = BoundsCheck { len : Operand :: Move ( len) , index : Operand :: Copy ( Place :: from ( index) ) } ;
732+ let msg = BoundsCheck { len, index : Operand :: Copy ( Place :: from ( index) ) } ;
733+
660734 // assert!(lt, "...")
661735 self . assert ( block, Operand :: Move ( lt) , true , msg, expr_span)
662736 }
0 commit comments