]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/normalize_array_len.rs
Auto merge of #107443 - cjgillot:generator-less-query, r=compiler-errors
[rust.git] / compiler / rustc_mir_transform / src / normalize_array_len.rs
1 //! This pass eliminates casting of arrays into slices when their length
2 //! is taken using `.len()` method. Handy to preserve information in MIR for const prop
3
4 use crate::ssa::SsaLocals;
5 use crate::MirPass;
6 use rustc_index::vec::IndexVec;
7 use rustc_middle::mir::visit::*;
8 use rustc_middle::mir::*;
9 use rustc_middle::ty::{self, TyCtxt};
10 use rustc_mir_dataflow::impls::borrowed_locals;
11
12 pub struct NormalizeArrayLen;
13
14 impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
15     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
16         sess.mir_opt_level() >= 3
17     }
18
19     #[instrument(level = "trace", skip(self, tcx, body))]
20     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
21         debug!(def_id = ?body.source.def_id());
22         normalize_array_len_calls(tcx, body)
23     }
24 }
25
26 fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
27     let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
28     let borrowed_locals = borrowed_locals(body);
29     let ssa = SsaLocals::new(tcx, param_env, body, &borrowed_locals);
30
31     let slice_lengths = compute_slice_length(tcx, &ssa, body);
32     debug!(?slice_lengths);
33
34     Replacer { tcx, slice_lengths }.visit_body_preserves_cfg(body);
35 }
36
37 fn compute_slice_length<'tcx>(
38     tcx: TyCtxt<'tcx>,
39     ssa: &SsaLocals,
40     body: &Body<'tcx>,
41 ) -> IndexVec<Local, Option<ty::Const<'tcx>>> {
42     let mut slice_lengths = IndexVec::from_elem(None, &body.local_decls);
43
44     for (local, rvalue) in ssa.assignments(body) {
45         match rvalue {
46             Rvalue::Cast(
47                 CastKind::Pointer(ty::adjustment::PointerCast::Unsize),
48                 operand,
49                 cast_ty,
50             ) => {
51                 let operand_ty = operand.ty(body, tcx);
52                 debug!(?operand_ty);
53                 if let Some(operand_ty) = operand_ty.builtin_deref(true)
54                     && let ty::Array(_, len) = operand_ty.ty.kind()
55                     && let Some(cast_ty) = cast_ty.builtin_deref(true)
56                     && let ty::Slice(..) = cast_ty.ty.kind()
57                 {
58                     slice_lengths[local] = Some(*len);
59                 }
60             }
61             // The length information is stored in the fat pointer, so we treat `operand` as a value.
62             Rvalue::Use(operand) => {
63                 if let Some(rhs) = operand.place() && let Some(rhs) = rhs.as_local() {
64                     slice_lengths[local] = slice_lengths[rhs];
65                 }
66             }
67             // The length information is stored in the fat pointer.
68             // Reborrowing copies length information from one pointer to the other.
69             Rvalue::Ref(_, _, rhs) | Rvalue::AddressOf(_, rhs) => {
70                 if let [PlaceElem::Deref] = rhs.projection[..] {
71                     slice_lengths[local] = slice_lengths[rhs.local];
72                 }
73             }
74             _ => {}
75         }
76     }
77
78     slice_lengths
79 }
80
81 struct Replacer<'tcx> {
82     tcx: TyCtxt<'tcx>,
83     slice_lengths: IndexVec<Local, Option<ty::Const<'tcx>>>,
84 }
85
86 impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
87     fn tcx(&self) -> TyCtxt<'tcx> {
88         self.tcx
89     }
90
91     fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, loc: Location) {
92         if let Rvalue::Len(place) = rvalue
93             && let [PlaceElem::Deref] = &place.projection[..]
94             && let Some(len) = self.slice_lengths[place.local]
95         {
96             *rvalue = Rvalue::Use(Operand::Constant(Box::new(Constant {
97                 span: rustc_span::DUMMY_SP,
98                 user_ty: None,
99                 literal: ConstantKind::from_const(len, self.tcx),
100             })));
101         }
102         self.super_rvalue(rvalue, loc);
103     }
104 }