1use rustc_hir as hir;
7use tracing::{debug, instrument};
8use ty::CoroutineArgsExt;
9
10use crate::mir::*;
11
12#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)]
13pub struct PlaceTy<'tcx> {
14 pub ty: Ty<'tcx>,
15 pub variant_index: Option<VariantIdx>,
17}
18
19#[cfg(target_pointer_width = "64")]
21rustc_data_structures::static_assert_size!(PlaceTy<'_>, 16);
22
23impl<'tcx> PlaceTy<'tcx> {
24 #[inline]
25 pub fn from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx> {
26 PlaceTy { ty, variant_index: None }
27 }
28
29 #[instrument(level = "debug", skip(tcx), ret)]
37 pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: FieldIdx) -> Ty<'tcx> {
38 if let Some(variant_index) = self.variant_index {
39 match *self.ty.kind() {
40 ty::Adt(adt_def, args) if adt_def.is_enum() => {
41 adt_def.variant(variant_index).fields[f].ty(tcx, args)
42 }
43 ty::Coroutine(def_id, args) => {
44 let mut variants = args.as_coroutine().state_tys(def_id, tcx);
45 let Some(mut variant) = variants.nth(variant_index.into()) else {
46 bug!("variant {variant_index:?} of coroutine out of range: {self:?}");
47 };
48
49 variant
50 .nth(f.index())
51 .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}"))
52 }
53 _ => bug!("can't downcast non-adt non-coroutine type: {self:?}"),
54 }
55 } else {
56 match self.ty.kind() {
57 ty::Adt(adt_def, args) if !adt_def.is_enum() => {
58 adt_def.non_enum_variant().fields[f].ty(tcx, args)
59 }
60 ty::Closure(_, args) => args
61 .as_closure()
62 .upvar_tys()
63 .get(f.index())
64 .copied()
65 .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
66 ty::CoroutineClosure(_, args) => args
67 .as_coroutine_closure()
68 .upvar_tys()
69 .get(f.index())
70 .copied()
71 .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
72 ty::Coroutine(_, args) => args
75 .as_coroutine()
76 .prefix_tys()
77 .get(f.index())
78 .copied()
79 .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
80 ty::Tuple(tys) => tys
81 .get(f.index())
82 .copied()
83 .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
84 _ => bug!("can't project out of {self:?}"),
85 }
86 }
87 }
88
89 pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
93 self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty)
94 }
95
96 pub fn projection_ty_core<V, T>(
102 self,
103 tcx: TyCtxt<'tcx>,
104 elem: &ProjectionElem<V, T>,
105 mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
106 mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
107 ) -> PlaceTy<'tcx>
108 where
109 V: ::std::fmt::Debug,
110 T: ::std::fmt::Debug + Copy,
111 {
112 if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
113 bug!("cannot use non field projection on downcasted place")
114 }
115 let answer = match *elem {
116 ProjectionElem::Deref => {
117 let ty = self.ty.builtin_deref(true).unwrap_or_else(|| {
118 bug!("deref projection of non-dereferenceable ty {:?}", self)
119 });
120 PlaceTy::from_ty(ty)
121 }
122 ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
123 PlaceTy::from_ty(self.ty.builtin_index().unwrap())
124 }
125 ProjectionElem::Subslice { from, to, from_end } => {
126 PlaceTy::from_ty(match self.ty.kind() {
127 ty::Slice(..) => self.ty,
128 ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from),
129 ty::Array(inner, size) if from_end => {
130 let size = size
131 .try_to_target_usize(tcx)
132 .expect("expected subslice projection on fixed-size array");
133 let len = size - from - to;
134 Ty::new_array(tcx, *inner, len)
135 }
136 _ => bug!("cannot subslice non-array type: `{:?}`", self),
137 })
138 }
139 ProjectionElem::Downcast(_name, index) => {
140 PlaceTy { ty: self.ty, variant_index: Some(index) }
141 }
142 ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
143 ProjectionElem::OpaqueCast(ty) => {
144 PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
145 }
146 ProjectionElem::Subtype(ty) => {
147 PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
148 }
149
150 ProjectionElem::UnwrapUnsafeBinder(ty) => {
152 PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
153 }
154 };
155 debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
156 answer
157 }
158}
159
160impl<'tcx> Place<'tcx> {
161 pub fn ty_from<D: ?Sized>(
162 local: Local,
163 projection: &[PlaceElem<'tcx>],
164 local_decls: &D,
165 tcx: TyCtxt<'tcx>,
166 ) -> PlaceTy<'tcx>
167 where
168 D: HasLocalDecls<'tcx>,
169 {
170 projection
171 .iter()
172 .fold(PlaceTy::from_ty(local_decls.local_decls()[local].ty), |place_ty, &elem| {
173 place_ty.projection_ty(tcx, elem)
174 })
175 }
176
177 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
178 where
179 D: HasLocalDecls<'tcx>,
180 {
181 Place::ty_from(self.local, self.projection, local_decls, tcx)
182 }
183}
184
185impl<'tcx> PlaceRef<'tcx> {
186 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
187 where
188 D: HasLocalDecls<'tcx>,
189 {
190 Place::ty_from(self.local, self.projection, local_decls, tcx)
191 }
192}
193
194pub enum RvalueInitializationState {
195 Shallow,
196 Deep,
197}
198
199impl<'tcx> Rvalue<'tcx> {
200 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
201 where
202 D: HasLocalDecls<'tcx>,
203 {
204 match *self {
205 Rvalue::Use(ref operand) => operand.ty(local_decls, tcx),
206 Rvalue::Repeat(ref operand, count) => {
207 Ty::new_array_with_const_len(tcx, operand.ty(local_decls, tcx), count)
208 }
209 Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did),
210 Rvalue::Ref(reg, bk, ref place) => {
211 let place_ty = place.ty(local_decls, tcx).ty;
212 Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy())
213 }
214 Rvalue::RawPtr(kind, ref place) => {
215 let place_ty = place.ty(local_decls, tcx).ty;
216 Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy())
217 }
218 Rvalue::Len(..) => tcx.types.usize,
219 Rvalue::Cast(.., ty) => ty,
220 Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => {
221 let lhs_ty = lhs.ty(local_decls, tcx);
222 let rhs_ty = rhs.ty(local_decls, tcx);
223 op.ty(tcx, lhs_ty, rhs_ty)
224 }
225 Rvalue::UnaryOp(op, ref operand) => {
226 let arg_ty = operand.ty(local_decls, tcx);
227 op.ty(tcx, arg_ty)
228 }
229 Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
230 Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
231 tcx.types.usize
232 }
233 Rvalue::NullaryOp(NullOp::ContractChecks, _)
234 | Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
235 Rvalue::Aggregate(ref ak, ref ops) => match **ak {
236 AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
237 AggregateKind::Tuple => {
238 Ty::new_tup_from_iter(tcx, ops.iter().map(|op| op.ty(local_decls, tcx)))
239 }
240 AggregateKind::Adt(did, _, args, _, _) => tcx.type_of(did).instantiate(tcx, args),
241 AggregateKind::Closure(did, args) => Ty::new_closure(tcx, did, args),
242 AggregateKind::Coroutine(did, args) => Ty::new_coroutine(tcx, did, args),
243 AggregateKind::CoroutineClosure(did, args) => {
244 Ty::new_coroutine_closure(tcx, did, args)
245 }
246 AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability),
247 },
248 Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
249 Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
250 Rvalue::WrapUnsafeBinder(_, ty) => ty,
251 }
252 }
253
254 #[inline]
255 pub fn initialization_state(&self) -> RvalueInitializationState {
258 match *self {
259 Rvalue::ShallowInitBox(_, _) => RvalueInitializationState::Shallow,
260 _ => RvalueInitializationState::Deep,
261 }
262 }
263}
264
265impl<'tcx> Operand<'tcx> {
266 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
267 where
268 D: HasLocalDecls<'tcx>,
269 {
270 match self {
271 &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
272 Operand::Constant(c) => c.const_.ty(),
273 }
274 }
275
276 pub fn span<D: ?Sized>(&self, local_decls: &D) -> Span
277 where
278 D: HasLocalDecls<'tcx>,
279 {
280 match self {
281 &Operand::Copy(ref l) | &Operand::Move(ref l) => {
282 local_decls.local_decls()[l.local].source_info.span
283 }
284 Operand::Constant(c) => c.span,
285 }
286 }
287}
288
289impl<'tcx> BinOp {
290 pub fn ty(&self, tcx: TyCtxt<'tcx>, lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>) -> Ty<'tcx> {
291 match self {
293 &BinOp::Add
294 | &BinOp::AddUnchecked
295 | &BinOp::Sub
296 | &BinOp::SubUnchecked
297 | &BinOp::Mul
298 | &BinOp::MulUnchecked
299 | &BinOp::Div
300 | &BinOp::Rem
301 | &BinOp::BitXor
302 | &BinOp::BitAnd
303 | &BinOp::BitOr => {
304 assert_eq!(lhs_ty, rhs_ty);
306 lhs_ty
307 }
308 &BinOp::AddWithOverflow | &BinOp::SubWithOverflow | &BinOp::MulWithOverflow => {
309 assert_eq!(lhs_ty, rhs_ty);
311 Ty::new_tup(tcx, &[lhs_ty, tcx.types.bool])
312 }
313 &BinOp::Shl
314 | &BinOp::ShlUnchecked
315 | &BinOp::Shr
316 | &BinOp::ShrUnchecked
317 | &BinOp::Offset => {
318 lhs_ty }
320 &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
321 tcx.types.bool
322 }
323 &BinOp::Cmp => {
324 assert_eq!(lhs_ty, rhs_ty);
326 tcx.ty_ordering_enum(None)
327 }
328 }
329 }
330}
331
332impl<'tcx> UnOp {
333 pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
334 match self {
335 UnOp::Not | UnOp::Neg => arg_ty,
336 UnOp::PtrMetadata => arg_ty.pointee_metadata_ty_or_projection(tcx),
337 }
338 }
339}
340
341impl BorrowKind {
342 pub fn to_mutbl_lossy(self) -> hir::Mutability {
343 match self {
344 BorrowKind::Mut { .. } => hir::Mutability::Mut,
345 BorrowKind::Shared => hir::Mutability::Not,
346
347 BorrowKind::Fake(_) => hir::Mutability::Not,
350 }
351 }
352}
353
354impl BinOp {
355 pub(crate) fn to_hir_binop(self) -> hir::BinOpKind {
356 match self {
357 BinOp::Add | BinOp::AddWithOverflow => hir::BinOpKind::Add,
360 BinOp::Sub | BinOp::SubWithOverflow => hir::BinOpKind::Sub,
361 BinOp::Mul | BinOp::MulWithOverflow => hir::BinOpKind::Mul,
362 BinOp::Div => hir::BinOpKind::Div,
363 BinOp::Rem => hir::BinOpKind::Rem,
364 BinOp::BitXor => hir::BinOpKind::BitXor,
365 BinOp::BitAnd => hir::BinOpKind::BitAnd,
366 BinOp::BitOr => hir::BinOpKind::BitOr,
367 BinOp::Shl => hir::BinOpKind::Shl,
368 BinOp::Shr => hir::BinOpKind::Shr,
369 BinOp::Eq => hir::BinOpKind::Eq,
370 BinOp::Ne => hir::BinOpKind::Ne,
371 BinOp::Lt => hir::BinOpKind::Lt,
372 BinOp::Gt => hir::BinOpKind::Gt,
373 BinOp::Le => hir::BinOpKind::Le,
374 BinOp::Ge => hir::BinOpKind::Ge,
375 BinOp::Cmp
377 | BinOp::AddUnchecked
378 | BinOp::SubUnchecked
379 | BinOp::MulUnchecked
380 | BinOp::ShlUnchecked
381 | BinOp::ShrUnchecked
382 | BinOp::Offset => {
383 unreachable!()
384 }
385 }
386 }
387
388 pub fn overflowing_to_wrapping(self) -> Option<BinOp> {
390 Some(match self {
391 BinOp::AddWithOverflow => BinOp::Add,
392 BinOp::SubWithOverflow => BinOp::Sub,
393 BinOp::MulWithOverflow => BinOp::Mul,
394 _ => return None,
395 })
396 }
397
398 pub fn is_overflowing(self) -> bool {
400 self.overflowing_to_wrapping().is_some()
401 }
402
403 pub fn wrapping_to_overflowing(self) -> Option<BinOp> {
405 Some(match self {
406 BinOp::Add => BinOp::AddWithOverflow,
407 BinOp::Sub => BinOp::SubWithOverflow,
408 BinOp::Mul => BinOp::MulWithOverflow,
409 _ => return None,
410 })
411 }
412}