1 /*! 2 * Methods for the various MIR types. These are intended for use after 3 * building is complete. 4 */ 5 6 use crate::mir::*; 7 use crate::ty::{self, Ty, TyCtxt}; 8 use rustc_hir as hir; 9 use rustc_target::abi::{FieldIdx, VariantIdx}; 10 11 #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] 12 pub struct PlaceTy<'tcx> { 13 pub ty: Ty<'tcx>, 14 /// Downcast to a particular variant of an enum or a generator, if included. 15 pub variant_index: Option<VariantIdx>, 16 } 17 18 // At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers. 19 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] 20 static_assert_size!(PlaceTy<'_>, 16); 21 22 impl<'tcx> PlaceTy<'tcx> { 23 #[inline] from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx>24 pub fn from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx> { 25 PlaceTy { ty, variant_index: None } 26 } 27 28 /// `place_ty.field_ty(tcx, f)` computes the type at a given field 29 /// of a record or enum-variant. (Most clients of `PlaceTy` can 30 /// instead just extract the relevant type directly from their 31 /// `PlaceElem`, but some instances of `ProjectionElem<V, T>` do 32 /// not carry a `Ty` for `T`.) 33 /// 34 /// Note that the resulting type has not been normalized. 35 #[instrument(level = "debug", skip(tcx), ret)] field_ty(self, tcx: TyCtxt<'tcx>, f: FieldIdx) -> Ty<'tcx>36 pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: FieldIdx) -> Ty<'tcx> { 37 match self.ty.kind() { 38 ty::Adt(adt_def, substs) => { 39 let variant_def = match self.variant_index { 40 None => adt_def.non_enum_variant(), 41 Some(variant_index) => { 42 assert!(adt_def.is_enum()); 43 &adt_def.variant(variant_index) 44 } 45 }; 46 let field_def = &variant_def.fields[f]; 47 field_def.ty(tcx, substs) 48 } 49 ty::Tuple(tys) => tys[f.index()], 50 _ => bug!("extracting field of non-tuple non-adt: {:?}", self), 51 } 52 } 53 54 /// Convenience wrapper around `projection_ty_core` for 55 /// `PlaceElem`, where we can just use the `Ty` that is already 56 /// stored inline on field projection elems. projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx>57 pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { 58 self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty) 59 } 60 61 /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` 62 /// projects `place_ty` onto `elem`, returning the appropriate 63 /// `Ty` or downcast variant corresponding to that projection. 64 /// The `handle_field` callback must map a `FieldIdx` to its `Ty`, 65 /// (which should be trivial when `T` = `Ty`). projection_ty_core<V, T>( self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, elem: &ProjectionElem<V, T>, mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>, mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>, ) -> PlaceTy<'tcx> where V: ::std::fmt::Debug, T: ::std::fmt::Debug + Copy,66 pub fn projection_ty_core<V, T>( 67 self, 68 tcx: TyCtxt<'tcx>, 69 param_env: ty::ParamEnv<'tcx>, 70 elem: &ProjectionElem<V, T>, 71 mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>, 72 mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>, 73 ) -> PlaceTy<'tcx> 74 where 75 V: ::std::fmt::Debug, 76 T: ::std::fmt::Debug + Copy, 77 { 78 if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) { 79 bug!("cannot use non field projection on downcasted place") 80 } 81 let answer = match *elem { 82 ProjectionElem::Deref => { 83 let ty = self 84 .ty 85 .builtin_deref(true) 86 .unwrap_or_else(|| { 87 bug!("deref projection of non-dereferenceable ty {:?}", self) 88 }) 89 .ty; 90 PlaceTy::from_ty(ty) 91 } 92 ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => { 93 PlaceTy::from_ty(self.ty.builtin_index().unwrap()) 94 } 95 ProjectionElem::Subslice { from, to, from_end } => { 96 PlaceTy::from_ty(match self.ty.kind() { 97 ty::Slice(..) => self.ty, 98 ty::Array(inner, _) if !from_end => { 99 Ty::new_array(tcx, *inner, (to - from) as u64) 100 } 101 ty::Array(inner, size) if from_end => { 102 let size = size.eval_target_usize(tcx, param_env); 103 let len = size - from - to; 104 Ty::new_array(tcx, *inner, len) 105 } 106 _ => bug!("cannot subslice non-array type: `{:?}`", self), 107 }) 108 } 109 ProjectionElem::Downcast(_name, index) => { 110 PlaceTy { ty: self.ty, variant_index: Some(index) } 111 } 112 ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)), 113 ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)), 114 }; 115 debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer); 116 answer 117 } 118 } 119 120 impl<'tcx> Place<'tcx> { ty_from<D: ?Sized>( local: Local, projection: &[PlaceElem<'tcx>], local_decls: &D, tcx: TyCtxt<'tcx>, ) -> PlaceTy<'tcx> where D: HasLocalDecls<'tcx>,121 pub fn ty_from<D: ?Sized>( 122 local: Local, 123 projection: &[PlaceElem<'tcx>], 124 local_decls: &D, 125 tcx: TyCtxt<'tcx>, 126 ) -> PlaceTy<'tcx> 127 where 128 D: HasLocalDecls<'tcx>, 129 { 130 projection 131 .iter() 132 .fold(PlaceTy::from_ty(local_decls.local_decls()[local].ty), |place_ty, &elem| { 133 place_ty.projection_ty(tcx, elem) 134 }) 135 } 136 ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx> where D: HasLocalDecls<'tcx>,137 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx> 138 where 139 D: HasLocalDecls<'tcx>, 140 { 141 Place::ty_from(self.local, &self.projection, local_decls, tcx) 142 } 143 } 144 145 impl<'tcx> PlaceRef<'tcx> { ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx> where D: HasLocalDecls<'tcx>,146 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx> 147 where 148 D: HasLocalDecls<'tcx>, 149 { 150 Place::ty_from(self.local, &self.projection, local_decls, tcx) 151 } 152 } 153 154 pub enum RvalueInitializationState { 155 Shallow, 156 Deep, 157 } 158 159 impl<'tcx> Rvalue<'tcx> { ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> where D: HasLocalDecls<'tcx>,160 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> 161 where 162 D: HasLocalDecls<'tcx>, 163 { 164 match *self { 165 Rvalue::Use(ref operand) => operand.ty(local_decls, tcx), 166 Rvalue::Repeat(ref operand, count) => { 167 Ty::new_array_with_const_len(tcx, operand.ty(local_decls, tcx), count) 168 } 169 Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did), 170 Rvalue::Ref(reg, bk, ref place) => { 171 let place_ty = place.ty(local_decls, tcx).ty; 172 Ty::new_ref(tcx, reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() }) 173 } 174 Rvalue::AddressOf(mutability, ref place) => { 175 let place_ty = place.ty(local_decls, tcx).ty; 176 Ty::new_ptr(tcx, ty::TypeAndMut { ty: place_ty, mutbl: mutability }) 177 } 178 Rvalue::Len(..) => tcx.types.usize, 179 Rvalue::Cast(.., ty) => ty, 180 Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { 181 let lhs_ty = lhs.ty(local_decls, tcx); 182 let rhs_ty = rhs.ty(local_decls, tcx); 183 op.ty(tcx, lhs_ty, rhs_ty) 184 } 185 Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => { 186 let lhs_ty = lhs.ty(local_decls, tcx); 187 let rhs_ty = rhs.ty(local_decls, tcx); 188 let ty = op.ty(tcx, lhs_ty, rhs_ty); 189 Ty::new_tup(tcx, &[ty, tcx.types.bool]) 190 } 191 Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), 192 Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx), 193 Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { 194 tcx.types.usize 195 } 196 Rvalue::Aggregate(ref ak, ref ops) => match **ak { 197 AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), 198 AggregateKind::Tuple => { 199 Ty::new_tup_from_iter(tcx, ops.iter().map(|op| op.ty(local_decls, tcx))) 200 } 201 AggregateKind::Adt(did, _, substs, _, _) => tcx.type_of(did).subst(tcx, substs), 202 AggregateKind::Closure(did, substs) => Ty::new_closure(tcx, did, substs), 203 AggregateKind::Generator(did, substs, movability) => { 204 Ty::new_generator(tcx, did, substs, movability) 205 } 206 }, 207 Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty), 208 Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, 209 } 210 } 211 212 #[inline] 213 /// Returns `true` if this rvalue is deeply initialized (most rvalues) or 214 /// whether its only shallowly initialized (`Rvalue::Box`). initialization_state(&self) -> RvalueInitializationState215 pub fn initialization_state(&self) -> RvalueInitializationState { 216 match *self { 217 Rvalue::ShallowInitBox(_, _) => RvalueInitializationState::Shallow, 218 _ => RvalueInitializationState::Deep, 219 } 220 } 221 } 222 223 impl<'tcx> Operand<'tcx> { ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> where D: HasLocalDecls<'tcx>,224 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> 225 where 226 D: HasLocalDecls<'tcx>, 227 { 228 match self { 229 &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty, 230 Operand::Constant(c) => c.literal.ty(), 231 } 232 } 233 } 234 235 impl<'tcx> BinOp { ty(&self, tcx: TyCtxt<'tcx>, lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>) -> Ty<'tcx>236 pub fn ty(&self, tcx: TyCtxt<'tcx>, lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>) -> Ty<'tcx> { 237 // FIXME: handle SIMD correctly 238 match self { 239 &BinOp::Add 240 | &BinOp::AddUnchecked 241 | &BinOp::Sub 242 | &BinOp::SubUnchecked 243 | &BinOp::Mul 244 | &BinOp::MulUnchecked 245 | &BinOp::Div 246 | &BinOp::Rem 247 | &BinOp::BitXor 248 | &BinOp::BitAnd 249 | &BinOp::BitOr => { 250 // these should be integers or floats of the same size. 251 assert_eq!(lhs_ty, rhs_ty); 252 lhs_ty 253 } 254 &BinOp::Shl 255 | &BinOp::ShlUnchecked 256 | &BinOp::Shr 257 | &BinOp::ShrUnchecked 258 | &BinOp::Offset => { 259 lhs_ty // lhs_ty can be != rhs_ty 260 } 261 &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => { 262 tcx.types.bool 263 } 264 } 265 } 266 } 267 268 impl BorrowKind { to_mutbl_lossy(self) -> hir::Mutability269 pub fn to_mutbl_lossy(self) -> hir::Mutability { 270 match self { 271 BorrowKind::Mut { .. } => hir::Mutability::Mut, 272 BorrowKind::Shared => hir::Mutability::Not, 273 274 // We have no type corresponding to a shallow borrow, so use 275 // `&` as an approximation. 276 BorrowKind::Shallow => hir::Mutability::Not, 277 } 278 } 279 } 280 281 impl BinOp { to_hir_binop(self) -> hir::BinOpKind282 pub fn to_hir_binop(self) -> hir::BinOpKind { 283 match self { 284 BinOp::Add => hir::BinOpKind::Add, 285 BinOp::Sub => hir::BinOpKind::Sub, 286 BinOp::Mul => hir::BinOpKind::Mul, 287 BinOp::Div => hir::BinOpKind::Div, 288 BinOp::Rem => hir::BinOpKind::Rem, 289 BinOp::BitXor => hir::BinOpKind::BitXor, 290 BinOp::BitAnd => hir::BinOpKind::BitAnd, 291 BinOp::BitOr => hir::BinOpKind::BitOr, 292 BinOp::Shl => hir::BinOpKind::Shl, 293 BinOp::Shr => hir::BinOpKind::Shr, 294 BinOp::Eq => hir::BinOpKind::Eq, 295 BinOp::Ne => hir::BinOpKind::Ne, 296 BinOp::Lt => hir::BinOpKind::Lt, 297 BinOp::Gt => hir::BinOpKind::Gt, 298 BinOp::Le => hir::BinOpKind::Le, 299 BinOp::Ge => hir::BinOpKind::Ge, 300 BinOp::AddUnchecked 301 | BinOp::SubUnchecked 302 | BinOp::MulUnchecked 303 | BinOp::ShlUnchecked 304 | BinOp::ShrUnchecked 305 | BinOp::Offset => { 306 unreachable!() 307 } 308 } 309 } 310 } 311