]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_const_eval/src/interpret/cast.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / compiler / rustc_const_eval / src / interpret / cast.rs
CommitLineData
923072b8 1use std::assert_matches::assert_matches;
ba9703b0 2use std::convert::TryFrom;
ea8adc8c 3
dfeec247
XL
4use rustc_apfloat::ieee::{Double, Single};
5use rustc_apfloat::{Float, FloatConvert};
ba9703b0
XL
6use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
7use rustc_middle::mir::CastKind;
8use rustc_middle::ty::adjustment::PointerCast;
c295e0f8 9use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
5869c6ff 10use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
064997fb 11use rustc_target::abi::Integer;
923072b8 12use rustc_type_ir::sty::TyKind::*;
f9f354fc 13
3dfed10e 14use super::{
29967ef6 15 util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy,
3dfed10e 16};
b7449926 17
ba9703b0 18impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
b7449926 19 pub fn cast(
8faf50e0 20 &mut self,
064997fb 21 src: &OpTy<'tcx, M::Provenance>,
f9f354fc
XL
22 cast_kind: CastKind,
23 cast_ty: Ty<'tcx>,
064997fb 24 dest: &PlaceTy<'tcx, M::Provenance>,
dc9dc135 25 ) -> InterpResult<'tcx> {
ba9703b0 26 use rustc_middle::mir::CastKind::*;
f9f354fc
XL
27 // FIXME: In which cases should we trigger UB when the source is uninit?
28 match cast_kind {
48663c56 29 Pointer(PointerCast::Unsize) => {
f9f354fc
XL
30 let cast_ty = self.layout_of(cast_ty)?;
31 self.unsize_into(src, cast_ty, dest)?;
8faf50e0
XL
32 }
33
923072b8
FG
34 PointerExposeAddress => {
35 let src = self.read_immediate(src)?;
36 let res = self.pointer_expose_address_cast(&src, cast_ty)?;
37 self.write_immediate(res, dest)?;
38 }
39
40 PointerFromExposedAddress => {
41 let src = self.read_immediate(src)?;
42 let res = self.pointer_from_exposed_address_cast(&src, cast_ty)?;
43 self.write_immediate(res, dest)?;
44 }
45
f9f354fc 46 Misc => {
a1dfa0c6 47 let src = self.read_immediate(src)?;
6a06907d 48 let res = self.misc_cast(&src, cast_ty)?;
416331ca 49 self.write_immediate(res, dest)?;
8faf50e0
XL
50 }
51
f9f354fc
XL
52 Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => {
53 // These are NOPs, but can be wide pointers.
54 let v = self.read_immediate(src)?;
55 self.write_immediate(*v, dest)?;
56 }
57
48663c56 58 Pointer(PointerCast::ReifyFnPointer) => {
b7449926 59 // The src operand does not matter, just its type
1b1a35ee 60 match *src.layout.ty.kind() {
b7449926 61 ty::FnDef(def_id, substs) => {
e1599b0c 62 // All reifications must be monomorphic, bail out otherwise.
3dfed10e 63 ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
e1599b0c 64
e74abb32
XL
65 let instance = ty::Instance::resolve_for_fn_ptr(
66 *self.tcx,
67 self.param_env,
68 def_id,
69 substs,
dfeec247
XL
70 )
71 .ok_or_else(|| err_inval!(TooGeneric))?;
e74abb32 72
04454e1e 73 let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
136023e0 74 self.write_pointer(fn_ptr, dest)?;
8faf50e0 75 }
f035d41b 76 _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty),
8faf50e0
XL
77 }
78 }
79
48663c56 80 Pointer(PointerCast::UnsafeFnPointer) => {
a1dfa0c6 81 let src = self.read_immediate(src)?;
1b1a35ee 82 match cast_ty.kind() {
b7449926
XL
83 ty::FnPtr(_) => {
84 // No change to value
a1dfa0c6 85 self.write_immediate(*src, dest)?;
8faf50e0 86 }
f035d41b 87 _ => span_bug!(self.cur_span(), "fn to unsafe fn cast on {:?}", cast_ty),
8faf50e0
XL
88 }
89 }
90
48663c56 91 Pointer(PointerCast::ClosureFnPointer(_)) => {
b7449926 92 // The src operand does not matter, just its type
1b1a35ee 93 match *src.layout.ty.kind() {
b7449926 94 ty::Closure(def_id, substs) => {
e1599b0c 95 // All reifications must be monomorphic, bail out otherwise.
3dfed10e 96 ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
e1599b0c 97
8faf50e0
XL
98 let instance = ty::Instance::resolve_closure(
99 *self.tcx,
100 def_id,
101 substs,
102 ty::ClosureKind::FnOnce,
064997fb
FG
103 )
104 .ok_or_else(|| err_inval!(TooGeneric))?;
04454e1e 105 let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
136023e0 106 self.write_pointer(fn_ptr, dest)?;
8faf50e0 107 }
f035d41b 108 _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty),
8faf50e0
XL
109 }
110 }
111 }
112 Ok(())
113 }
114
5e7ed085 115 pub fn misc_cast(
04454e1e 116 &mut self,
064997fb 117 src: &ImmTy<'tcx, M::Provenance>,
f9f354fc 118 cast_ty: Ty<'tcx>,
064997fb 119 ) -> InterpResult<'tcx, Immediate<M::Provenance>> {
923072b8 120 use rustc_type_ir::sty::TyKind::*;
f9f354fc 121 trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty);
ea8adc8c 122
1b1a35ee 123 match src.layout.ty.kind() {
dc9dc135 124 // Floating point
dfeec247 125 Float(FloatTy::F32) => {
f9f354fc 126 return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into());
dfeec247
XL
127 }
128 Float(FloatTy::F64) => {
f9f354fc 129 return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into());
dfeec247 130 }
064997fb 131 // The rest is integer/pointer-"like", including fn ptr casts
dfeec247
XL
132 _ => assert!(
133 src.layout.ty.is_bool()
134 || src.layout.ty.is_char()
dfeec247
XL
135 || src.layout.ty.is_integral()
136 || src.layout.ty.is_any_ptr(),
137 "Unexpected cast from type {:?}",
138 src.layout.ty
139 ),
416331ca
XL
140 }
141
f9f354fc
XL
142 // # First handle non-scalar source values.
143
416331ca 144 // Handle casting any ptr to raw ptr (might be a fat ptr).
f9f354fc
XL
145 if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() {
146 let dest_layout = self.layout_of(cast_ty)?;
147 if dest_layout.size == src.layout.size {
148 // Thin or fat pointer that just hast the ptr kind of target type changed.
6a06907d 149 return Ok(**src);
f9f354fc
XL
150 } else {
151 // Casting the metadata away from a fat ptr.
04454e1e
FG
152 assert_eq!(src.layout.size, 2 * self.pointer_size());
153 assert_eq!(dest_layout.size, self.pointer_size());
f9f354fc 154 assert!(src.layout.ty.is_unsafe_ptr());
6a06907d 155 return match **src {
064997fb 156 Immediate::ScalarPair(data, _) => Ok(data.check_init()?.into()),
f035d41b
XL
157 Immediate::Scalar(..) => span_bug!(
158 self.cur_span(),
f9f354fc
XL
159 "{:?} input to a fat-to-thin cast ({:?} -> {:?})",
160 *src,
161 src.layout.ty,
162 cast_ty
163 ),
064997fb 164 Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
f9f354fc
XL
165 };
166 }
ea8adc8c 167 }
416331ca 168
5e7ed085 169 // # The remaining source values are scalar and "int-like".
04454e1e 170 let scalar = src.to_scalar()?;
923072b8
FG
171 Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into())
172 }
04454e1e 173
923072b8
FG
174 pub fn pointer_expose_address_cast(
175 &mut self,
064997fb 176 src: &ImmTy<'tcx, M::Provenance>,
923072b8 177 cast_ty: Ty<'tcx>,
064997fb 178 ) -> InterpResult<'tcx, Immediate<M::Provenance>> {
923072b8
FG
179 assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_));
180 assert!(cast_ty.is_integral());
f9f354fc 181
923072b8 182 let scalar = src.to_scalar()?;
064997fb 183 let ptr = scalar.to_pointer(self)?;
923072b8
FG
184 match ptr.into_pointer_or_addr() {
185 Ok(ptr) => M::expose_ptr(self, ptr)?,
064997fb 186 Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP.
923072b8 187 };
04454e1e 188 Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into())
ea8adc8c
XL
189 }
190
923072b8
FG
191 pub fn pointer_from_exposed_address_cast(
192 &mut self,
064997fb 193 src: &ImmTy<'tcx, M::Provenance>,
923072b8 194 cast_ty: Ty<'tcx>,
064997fb 195 ) -> InterpResult<'tcx, Immediate<M::Provenance>> {
923072b8
FG
196 assert!(src.layout.ty.is_integral());
197 assert_matches!(cast_ty.kind(), ty::RawPtr(_));
198
199 // First cast to usize.
200 let scalar = src.to_scalar()?;
201 let addr = self.cast_from_int_like(scalar, src.layout, self.tcx.types.usize)?;
202 let addr = addr.to_machine_usize(self)?;
203
204 // Then turn address into pointer.
205 let ptr = M::ptr_from_addr_cast(&self, addr)?;
206 Ok(Scalar::from_maybe_pointer(ptr, self).into())
207 }
208
04454e1e 209 pub fn cast_from_int_like(
ea8adc8c 210 &self,
064997fb 211 scalar: Scalar<M::Provenance>, // input value (there is no ScalarTy so we separate data+layout)
ba9703b0 212 src_layout: TyAndLayout<'tcx>,
f9f354fc 213 cast_ty: Ty<'tcx>,
064997fb 214 ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
dc9dc135 215 // Let's make sure v is sign-extended *if* it has a signed type.
f9f354fc 216 let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`.
04454e1e
FG
217
218 let v = scalar.to_bits(src_layout.size)?;
dfeec247 219 let v = if signed { self.sign_extend(v, src_layout) } else { v };
f9f354fc 220 trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty);
04454e1e
FG
221
222 Ok(match *cast_ty.kind() {
223 Int(_) | Uint(_) => {
1b1a35ee 224 let size = match *cast_ty.kind() {
5869c6ff
XL
225 Int(t) => Integer::from_int_ty(self, t).size(),
226 Uint(t) => Integer::from_uint_ty(self, t).size(),
f9f354fc
XL
227 _ => bug!(),
228 };
29967ef6 229 let v = size.truncate(v);
f9f354fc 230 Scalar::from_uint(v, size)
0531ce1d 231 }
ea8adc8c 232
f9f354fc
XL
233 Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value),
234 Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value),
235 Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value),
236 Float(FloatTy::F64) => Scalar::from_f64(Double::from_u128(v).value),
ea8adc8c 237
b7449926
XL
238 Char => {
239 // `u8` to `char` cast
f9f354fc 240 Scalar::from_u32(u8::try_from(v).unwrap().into())
dfeec247 241 }
ea8adc8c 242
0531ce1d 243 // Casts to bool are not permitted by rustc, no need to handle them here.
f035d41b 244 _ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty),
04454e1e 245 })
ea8adc8c
XL
246 }
247
064997fb 248 fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance>
dfeec247 249 where
064997fb 250 F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>,
dc9dc135 251 {
923072b8 252 use rustc_type_ir::sty::TyKind::*;
1b1a35ee 253 match *dest_ty.kind() {
0531ce1d 254 // float -> uint
b7449926 255 Uint(t) => {
5869c6ff 256 let size = Integer::from_uint_ty(self, t).size();
ba9703b0
XL
257 // `to_u128` is a saturating cast, which is what we need
258 // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r).
f9f354fc 259 let v = f.to_u128(size.bits_usize()).value;
b7449926 260 // This should already fit the bit width
f9f354fc 261 Scalar::from_uint(v, size)
dfeec247 262 }
0531ce1d 263 // float -> int
b7449926 264 Int(t) => {
5869c6ff 265 let size = Integer::from_int_ty(self, t).size();
ba9703b0
XL
266 // `to_i128` is a saturating cast, which is what we need
267 // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r).
f9f354fc
XL
268 let v = f.to_i128(size.bits_usize()).value;
269 Scalar::from_int(v, size)
dfeec247 270 }
dc9dc135 271 // float -> f32
f9f354fc 272 Float(FloatTy::F32) => Scalar::from_f32(f.convert(&mut false).value),
dc9dc135 273 // float -> f64
f9f354fc 274 Float(FloatTy::F64) => Scalar::from_f64(f.convert(&mut false).value),
dc9dc135 275 // That's it.
f035d41b 276 _ => span_bug!(self.cur_span(), "invalid float to {:?} cast", dest_ty),
ea8adc8c
XL
277 }
278 }
279
b7449926
XL
280 fn unsize_into_ptr(
281 &mut self,
064997fb
FG
282 src: &OpTy<'tcx, M::Provenance>,
283 dest: &PlaceTy<'tcx, M::Provenance>,
b7449926 284 // The pointee types
e74abb32 285 source_ty: Ty<'tcx>,
f9f354fc 286 cast_ty: Ty<'tcx>,
dc9dc135 287 ) -> InterpResult<'tcx> {
b7449926 288 // A<Struct> -> A<Trait> conversion
416331ca 289 let (src_pointee_ty, dest_pointee_ty) =
f9f354fc 290 self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env);
b7449926 291
1b1a35ee 292 match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) {
b7449926 293 (&ty::Array(_, length), &ty::Slice(_)) => {
60c5eb7d 294 let ptr = self.read_immediate(src)?.to_scalar()?;
b7449926 295 // u64 cast is from usize to u64, which is always good
f035d41b
XL
296 let val =
297 Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
a1dfa0c6 298 self.write_immediate(val, dest)
b7449926 299 }
94222f64 300 (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
064997fb
FG
301 let (old_data, old_vptr) = self.read_immediate(src)?.to_scalar_pair()?;
302 let old_vptr = old_vptr.to_pointer(self)?;
303 let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
304 if old_trait != data_a.principal() {
305 throw_ub_format!("upcast on a pointer whose vtable does not match its type");
94222f64 306 }
064997fb
FG
307 let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
308 self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
b7449926
XL
309 }
310 (_, &ty::Dynamic(ref data, _)) => {
311 // Initial cast from sized to dyn trait
064997fb 312 let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
60c5eb7d 313 let ptr = self.read_immediate(src)?.to_scalar()?;
136023e0 314 let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
a1dfa0c6 315 self.write_immediate(val, dest)
b7449926
XL
316 }
317
f035d41b
XL
318 _ => {
319 span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty)
320 }
b7449926
XL
321 }
322 }
323
324 fn unsize_into(
325 &mut self,
064997fb 326 src: &OpTy<'tcx, M::Provenance>,
f9f354fc 327 cast_ty: TyAndLayout<'tcx>,
064997fb 328 dest: &PlaceTy<'tcx, M::Provenance>,
dc9dc135 329 ) -> InterpResult<'tcx> {
f9f354fc 330 trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty);
1b1a35ee 331 match (&src.layout.ty.kind(), &cast_ty.ty.kind()) {
f9f354fc
XL
332 (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. }))
333 | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => {
5099ac24 334 self.unsize_into_ptr(src, dest, *s, *c)
b7449926
XL
335 }
336 (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
337 assert_eq!(def_a, def_b);
b7449926
XL
338
339 // unsizing of generic struct with pointer fields
340 // Example: `Arc<T>` -> `Arc<Trait>`
341 // here we need to increase the size of every &T thin ptr field to a fat ptr
342 for i in 0..src.layout.fields.count() {
94222f64 343 let cast_ty_field = cast_ty.field(self, i);
f9f354fc 344 if cast_ty_field.is_zst() {
b7449926
XL
345 continue;
346 }
ba9703b0 347 let src_field = self.operand_field(src, i)?;
f9f354fc
XL
348 let dst_field = self.place_field(dest, i)?;
349 if src_field.layout.ty == cast_ty_field.ty {
064997fb 350 self.copy_op(&src_field, &dst_field, /*allow_transmute*/ false)?;
b7449926 351 } else {
6a06907d 352 self.unsize_into(&src_field, cast_ty_field, &dst_field)?;
b7449926
XL
353 }
354 }
355 Ok(())
356 }
f035d41b
XL
357 _ => span_bug!(
358 self.cur_span(),
359 "unsize_into: invalid conversion: {:?} -> {:?}",
360 src.layout,
361 dest.layout
362 ),
b7449926
XL
363 }
364 }
ea8adc8c 365}