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 multi_projection_ty(
90 self,
91 tcx: TyCtxt<'tcx>,
92 elems: &[PlaceElem<'tcx>],
93 ) -> PlaceTy<'tcx> {
94 elems.iter().fold(self, |place_ty, &elem| place_ty.projection_ty(tcx, elem))
95 }
96
97 pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
101 self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty)
102 }
103
104 pub fn projection_ty_core<V, T>(
110 self,
111 tcx: TyCtxt<'tcx>,
112 elem: &ProjectionElem<V, T>,
113 mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
114 mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
115 ) -> PlaceTy<'tcx>
116 where
117 V: ::std::fmt::Debug,
118 T: ::std::fmt::Debug + Copy,
119 {
120 if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
121 bug!("cannot use non field projection on downcasted place")
122 }
123 let answer = match *elem {
124 ProjectionElem::Deref => {
125 let ty = self.ty.builtin_deref(true).unwrap_or_else(|| {
126 bug!("deref projection of non-dereferenceable ty {:?}", self)
127 });
128 PlaceTy::from_ty(ty)
129 }
130 ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
131 PlaceTy::from_ty(self.ty.builtin_index().unwrap())
132 }
133 ProjectionElem::Subslice { from, to, from_end } => {
134 PlaceTy::from_ty(match self.ty.kind() {
135 ty::Slice(..) => self.ty,
136 ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from),
137 ty::Array(inner, size) if from_end => {
138 let size = size
139 .try_to_target_usize(tcx)
140 .expect("expected subslice projection on fixed-size array");
141 let len = size - from - to;
142 Ty::new_array(tcx, *inner, len)
143 }
144 _ => bug!("cannot subslice non-array type: `{:?}`", self),
145 })
146 }
147 ProjectionElem::Downcast(_name, index) => {
148 PlaceTy { ty: self.ty, variant_index: Some(index) }
149 }
150 ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
151 ProjectionElem::OpaqueCast(ty) => {
152 PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
153 }
154 ProjectionElem::Subtype(ty) => {
155 PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
156 }
157
158 ProjectionElem::UnwrapUnsafeBinder(ty) => {
160 PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
161 }
162 };
163 debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
164 answer
165 }
166}
167
168impl<'tcx> Place<'tcx> {
169 pub fn ty_from<D: ?Sized>(
170 local: Local,
171 projection: &[PlaceElem<'tcx>],
172 local_decls: &D,
173 tcx: TyCtxt<'tcx>,
174 ) -> PlaceTy<'tcx>
175 where
176 D: HasLocalDecls<'tcx>,
177 {
178 PlaceTy::from_ty(local_decls.local_decls()[local].ty).multi_projection_ty(tcx, projection)
179 }
180
181 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
182 where
183 D: HasLocalDecls<'tcx>,
184 {
185 Place::ty_from(self.local, self.projection, local_decls, tcx)
186 }
187}
188
189impl<'tcx> PlaceRef<'tcx> {
190 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
191 where
192 D: HasLocalDecls<'tcx>,
193 {
194 Place::ty_from(self.local, self.projection, local_decls, tcx)
195 }
196}
197
198pub enum RvalueInitializationState {
199 Shallow,
200 Deep,
201}
202
203impl<'tcx> Rvalue<'tcx> {
204 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
205 where
206 D: HasLocalDecls<'tcx>,
207 {
208 match *self {
209 Rvalue::Use(ref operand) => operand.ty(local_decls, tcx),
210 Rvalue::Repeat(ref operand, count) => {
211 Ty::new_array_with_const_len(tcx, operand.ty(local_decls, tcx), count)
212 }
213 Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did),
214 Rvalue::Ref(reg, bk, ref place) => {
215 let place_ty = place.ty(local_decls, tcx).ty;
216 Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy())
217 }
218 Rvalue::RawPtr(kind, ref place) => {
219 let place_ty = place.ty(local_decls, tcx).ty;
220 Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy())
221 }
222 Rvalue::Len(..) => tcx.types.usize,
223 Rvalue::Cast(.., ty) => ty,
224 Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => {
225 let lhs_ty = lhs.ty(local_decls, tcx);
226 let rhs_ty = rhs.ty(local_decls, tcx);
227 op.ty(tcx, lhs_ty, rhs_ty)
228 }
229 Rvalue::UnaryOp(op, ref operand) => {
230 let arg_ty = operand.ty(local_decls, tcx);
231 op.ty(tcx, arg_ty)
232 }
233 Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
234 Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
235 tcx.types.usize
236 }
237 Rvalue::NullaryOp(NullOp::ContractChecks, _)
238 | Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
239 Rvalue::Aggregate(ref ak, ref ops) => match **ak {
240 AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
241 AggregateKind::Tuple => {
242 Ty::new_tup_from_iter(tcx, ops.iter().map(|op| op.ty(local_decls, tcx)))
243 }
244 AggregateKind::Adt(did, _, args, _, _) => tcx.type_of(did).instantiate(tcx, args),
245 AggregateKind::Closure(did, args) => Ty::new_closure(tcx, did, args),
246 AggregateKind::Coroutine(did, args) => Ty::new_coroutine(tcx, did, args),
247 AggregateKind::CoroutineClosure(did, args) => {
248 Ty::new_coroutine_closure(tcx, did, args)
249 }
250 AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability),
251 },
252 Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
253 Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
254 Rvalue::WrapUnsafeBinder(_, ty) => ty,
255 }
256 }
257
258 #[inline]
259 pub fn initialization_state(&self) -> RvalueInitializationState {
262 match *self {
263 Rvalue::ShallowInitBox(_, _) => RvalueInitializationState::Shallow,
264 _ => RvalueInitializationState::Deep,
265 }
266 }
267}
268
269impl<'tcx> Operand<'tcx> {
270 pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
271 where
272 D: HasLocalDecls<'tcx>,
273 {
274 match self {
275 &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
276 Operand::Constant(c) => c.const_.ty(),
277 }
278 }
279
280 pub fn span<D: ?Sized>(&self, local_decls: &D) -> Span
281 where
282 D: HasLocalDecls<'tcx>,
283 {
284 match self {
285 &Operand::Copy(ref l) | &Operand::Move(ref l) => {
286 local_decls.local_decls()[l.local].source_info.span
287 }
288 Operand::Constant(c) => c.span,
289 }
290 }
291}
292
293impl<'tcx> BinOp {
294 pub fn ty(&self, tcx: TyCtxt<'tcx>, lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>) -> Ty<'tcx> {
295 match self {
297 &BinOp::Add
298 | &BinOp::AddUnchecked
299 | &BinOp::Sub
300 | &BinOp::SubUnchecked
301 | &BinOp::Mul
302 | &BinOp::MulUnchecked
303 | &BinOp::Div
304 | &BinOp::Rem
305 | &BinOp::BitXor
306 | &BinOp::BitAnd
307 | &BinOp::BitOr => {
308 assert_eq!(lhs_ty, rhs_ty);
310 lhs_ty
311 }
312 &BinOp::AddWithOverflow | &BinOp::SubWithOverflow | &BinOp::MulWithOverflow => {
313 assert_eq!(lhs_ty, rhs_ty);
315 Ty::new_tup(tcx, &[lhs_ty, tcx.types.bool])
316 }
317 &BinOp::Shl
318 | &BinOp::ShlUnchecked
319 | &BinOp::Shr
320 | &BinOp::ShrUnchecked
321 | &BinOp::Offset => {
322 lhs_ty }
324 &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
325 tcx.types.bool
326 }
327 &BinOp::Cmp => {
328 assert_eq!(lhs_ty, rhs_ty);
330 tcx.ty_ordering_enum(None)
331 }
332 }
333 }
334}
335
336impl<'tcx> UnOp {
337 pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
338 match self {
339 UnOp::Not | UnOp::Neg => arg_ty,
340 UnOp::PtrMetadata => arg_ty.pointee_metadata_ty_or_projection(tcx),
341 }
342 }
343}
344
345impl BorrowKind {
346 pub fn to_mutbl_lossy(self) -> hir::Mutability {
347 match self {
348 BorrowKind::Mut { .. } => hir::Mutability::Mut,
349 BorrowKind::Shared => hir::Mutability::Not,
350
351 BorrowKind::Fake(_) => hir::Mutability::Not,
354 }
355 }
356}
357
358impl BinOp {
359 pub(crate) fn to_hir_binop(self) -> hir::BinOpKind {
360 match self {
361 BinOp::Add | BinOp::AddWithOverflow => hir::BinOpKind::Add,
364 BinOp::Sub | BinOp::SubWithOverflow => hir::BinOpKind::Sub,
365 BinOp::Mul | BinOp::MulWithOverflow => hir::BinOpKind::Mul,
366 BinOp::Div => hir::BinOpKind::Div,
367 BinOp::Rem => hir::BinOpKind::Rem,
368 BinOp::BitXor => hir::BinOpKind::BitXor,
369 BinOp::BitAnd => hir::BinOpKind::BitAnd,
370 BinOp::BitOr => hir::BinOpKind::BitOr,
371 BinOp::Shl => hir::BinOpKind::Shl,
372 BinOp::Shr => hir::BinOpKind::Shr,
373 BinOp::Eq => hir::BinOpKind::Eq,
374 BinOp::Ne => hir::BinOpKind::Ne,
375 BinOp::Lt => hir::BinOpKind::Lt,
376 BinOp::Gt => hir::BinOpKind::Gt,
377 BinOp::Le => hir::BinOpKind::Le,
378 BinOp::Ge => hir::BinOpKind::Ge,
379 BinOp::Cmp
381 | BinOp::AddUnchecked
382 | BinOp::SubUnchecked
383 | BinOp::MulUnchecked
384 | BinOp::ShlUnchecked
385 | BinOp::ShrUnchecked
386 | BinOp::Offset => {
387 unreachable!()
388 }
389 }
390 }
391
392 pub fn overflowing_to_wrapping(self) -> Option<BinOp> {
394 Some(match self {
395 BinOp::AddWithOverflow => BinOp::Add,
396 BinOp::SubWithOverflow => BinOp::Sub,
397 BinOp::MulWithOverflow => BinOp::Mul,
398 _ => return None,
399 })
400 }
401
402 pub fn is_overflowing(self) -> bool {
404 self.overflowing_to_wrapping().is_some()
405 }
406
407 pub fn wrapping_to_overflowing(self) -> Option<BinOp> {
409 Some(match self {
410 BinOp::Add => BinOp::AddWithOverflow,
411 BinOp::Sub => BinOp::SubWithOverflow,
412 BinOp::Mul => BinOp::MulWithOverflow,
413 _ => return None,
414 })
415 }
416}