]>
Commit | Line | Data |
---|---|---|
ea8adc8c | 1 | use rustc::mir; |
ff7c6d11 XL |
2 | use rustc::ty::{self, Ty}; |
3 | use rustc::ty::layout::LayoutOf; | |
ea8adc8c | 4 | use syntax::codemap::Span; |
83c7162d | 5 | use rustc_target::spec::abi::Abi; |
ea8adc8c | 6 | |
ff7c6d11 | 7 | use rustc::mir::interpret::{EvalResult, PrimVal, Value}; |
0531ce1d | 8 | use super::{EvalContext, Place, Machine, ValTy}; |
ea8adc8c XL |
9 | |
10 | use rustc_data_structures::indexed_vec::Idx; | |
ff7c6d11 | 11 | use interpret::memory::HasMemory; |
ea8adc8c XL |
12 | |
13 | mod drop; | |
14 | ||
0531ce1d | 15 | impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { |
ea8adc8c XL |
16 | pub fn goto_block(&mut self, target: mir::BasicBlock) { |
17 | self.frame_mut().block = target; | |
18 | self.frame_mut().stmt = 0; | |
19 | } | |
20 | ||
21 | pub(super) fn eval_terminator( | |
22 | &mut self, | |
23 | terminator: &mir::Terminator<'tcx>, | |
24 | ) -> EvalResult<'tcx> { | |
25 | use rustc::mir::TerminatorKind::*; | |
26 | match terminator.kind { | |
27 | Return => { | |
ff7c6d11 | 28 | self.dump_local(self.frame().return_place); |
ea8adc8c XL |
29 | self.pop_stack_frame()? |
30 | } | |
31 | ||
32 | Goto { target } => self.goto_block(target), | |
33 | ||
34 | SwitchInt { | |
35 | ref discr, | |
36 | ref values, | |
37 | ref targets, | |
38 | .. | |
39 | } => { | |
ea8adc8c XL |
40 | let discr_val = self.eval_operand(discr)?; |
41 | let discr_prim = self.value_to_primval(discr_val)?; | |
42 | ||
43 | // Branch to the `otherwise` case by default, if no match is found. | |
44 | let mut target_block = targets[targets.len() - 1]; | |
45 | ||
0531ce1d | 46 | for (index, &const_int) in values.iter().enumerate() { |
83c7162d | 47 | if discr_prim.to_bytes()? == const_int { |
ea8adc8c XL |
48 | target_block = targets[index]; |
49 | break; | |
50 | } | |
51 | } | |
52 | ||
53 | self.goto_block(target_block); | |
54 | } | |
55 | ||
56 | Call { | |
57 | ref func, | |
58 | ref args, | |
59 | ref destination, | |
60 | .. | |
61 | } => { | |
62 | let destination = match *destination { | |
ff7c6d11 | 63 | Some((ref lv, target)) => Some((self.eval_place(lv)?, target)), |
ea8adc8c XL |
64 | None => None, |
65 | }; | |
66 | ||
ff7c6d11 XL |
67 | let func = self.eval_operand(func)?; |
68 | let (fn_def, sig) = match func.ty.sty { | |
ea8adc8c | 69 | ty::TyFnPtr(sig) => { |
ff7c6d11 | 70 | let fn_ptr = self.value_to_primval(func)?.to_ptr()?; |
ea8adc8c | 71 | let instance = self.memory.get_fn(fn_ptr)?; |
0531ce1d | 72 | let instance_ty = instance.ty(*self.tcx); |
ea8adc8c XL |
73 | match instance_ty.sty { |
74 | ty::TyFnDef(..) => { | |
0531ce1d XL |
75 | let real_sig = instance_ty.fn_sig(*self.tcx); |
76 | let sig = self.tcx.normalize_erasing_late_bound_regions( | |
77 | ty::ParamEnv::reveal_all(), | |
78 | &sig, | |
79 | ); | |
80 | let real_sig = self.tcx.normalize_erasing_late_bound_regions( | |
81 | ty::ParamEnv::reveal_all(), | |
82 | &real_sig, | |
83 | ); | |
ea8adc8c XL |
84 | if !self.check_sig_compat(sig, real_sig)? { |
85 | return err!(FunctionPointerTyMismatch(real_sig, sig)); | |
86 | } | |
87 | } | |
88 | ref other => bug!("instance def ty: {:?}", other), | |
89 | } | |
90 | (instance, sig) | |
91 | } | |
92 | ty::TyFnDef(def_id, substs) => ( | |
ff7c6d11 | 93 | self.resolve(def_id, substs)?, |
0531ce1d | 94 | func.ty.fn_sig(*self.tcx), |
ea8adc8c XL |
95 | ), |
96 | _ => { | |
ff7c6d11 | 97 | let msg = format!("can't handle callee of type {:?}", func.ty); |
ea8adc8c XL |
98 | return err!(Unimplemented(msg)); |
99 | } | |
100 | }; | |
101 | let args = self.operands_to_args(args)?; | |
0531ce1d XL |
102 | let sig = self.tcx.normalize_erasing_late_bound_regions( |
103 | ty::ParamEnv::reveal_all(), | |
104 | &sig, | |
105 | ); | |
ea8adc8c XL |
106 | self.eval_fn_call( |
107 | fn_def, | |
108 | destination, | |
109 | &args, | |
110 | terminator.source_info.span, | |
111 | sig, | |
112 | )?; | |
113 | } | |
114 | ||
115 | Drop { | |
116 | ref location, | |
117 | target, | |
118 | .. | |
119 | } => { | |
120 | // FIXME(CTFE): forbid drop in const eval | |
ff7c6d11 XL |
121 | let place = self.eval_place(location)?; |
122 | let ty = self.place_ty(location); | |
0531ce1d XL |
123 | let ty = self.tcx.subst_and_normalize_erasing_regions( |
124 | self.substs(), | |
125 | ty::ParamEnv::reveal_all(), | |
126 | &ty, | |
127 | ); | |
ea8adc8c XL |
128 | trace!("TerminatorKind::drop: {:?}, type {}", location, ty); |
129 | ||
0531ce1d | 130 | let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty); |
ff7c6d11 XL |
131 | self.drop_place( |
132 | place, | |
ea8adc8c XL |
133 | instance, |
134 | ty, | |
135 | terminator.source_info.span, | |
136 | target, | |
137 | )?; | |
138 | } | |
139 | ||
140 | Assert { | |
141 | ref cond, | |
142 | expected, | |
143 | ref msg, | |
144 | target, | |
145 | .. | |
146 | } => { | |
147 | let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; | |
148 | if expected == cond_val { | |
149 | self.goto_block(target); | |
150 | } else { | |
83c7162d | 151 | use rustc::mir::interpret::EvalErrorKind::*; |
ea8adc8c XL |
152 | return match *msg { |
153 | BoundsCheck { ref len, ref index } => { | |
ea8adc8c XL |
154 | let len = self.eval_operand_to_primval(len) |
155 | .expect("can't eval len") | |
156 | .to_u64()?; | |
157 | let index = self.eval_operand_to_primval(index) | |
158 | .expect("can't eval index") | |
159 | .to_u64()?; | |
83c7162d | 160 | err!(BoundsCheck { len, index }) |
ea8adc8c | 161 | } |
83c7162d XL |
162 | Overflow(op) => Err(Overflow(op).into()), |
163 | OverflowNeg => Err(OverflowNeg.into()), | |
164 | DivisionByZero => Err(DivisionByZero.into()), | |
165 | RemainderByZero => Err(RemainderByZero.into()), | |
ea8adc8c XL |
166 | GeneratorResumedAfterReturn | |
167 | GeneratorResumedAfterPanic => unimplemented!(), | |
83c7162d | 168 | _ => bug!(), |
ea8adc8c XL |
169 | }; |
170 | } | |
171 | } | |
172 | ||
173 | Yield { .. } => unimplemented!("{:#?}", terminator.kind), | |
174 | GeneratorDrop => unimplemented!(), | |
175 | DropAndReplace { .. } => unimplemented!(), | |
176 | Resume => unimplemented!(), | |
ff7c6d11 XL |
177 | Abort => unimplemented!(), |
178 | FalseEdges { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"), | |
2c00a5a8 | 179 | FalseUnwind { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"), |
ea8adc8c XL |
180 | Unreachable => return err!(Unreachable), |
181 | } | |
182 | ||
183 | Ok(()) | |
184 | } | |
185 | ||
186 | /// Decides whether it is okay to call the method with signature `real_sig` using signature `sig`. | |
187 | /// FIXME: This should take into account the platform-dependent ABI description. | |
188 | fn check_sig_compat( | |
189 | &mut self, | |
190 | sig: ty::FnSig<'tcx>, | |
191 | real_sig: ty::FnSig<'tcx>, | |
192 | ) -> EvalResult<'tcx, bool> { | |
ff7c6d11 | 193 | fn check_ty_compat<'tcx>(ty: Ty<'tcx>, real_ty: Ty<'tcx>) -> bool { |
ea8adc8c XL |
194 | if ty == real_ty { |
195 | return true; | |
196 | } // This is actually a fast pointer comparison | |
197 | return match (&ty.sty, &real_ty.sty) { | |
198 | // Permit changing the pointer type of raw pointers and references as well as | |
199 | // mutability of raw pointers. | |
200 | // TODO: Should not be allowed when fat pointers are involved. | |
ff7c6d11 XL |
201 | (&ty::TyRawPtr(_), &ty::TyRawPtr(_)) => true, |
202 | (&ty::TyRef(_, _), &ty::TyRef(_, _)) => { | |
ea8adc8c XL |
203 | ty.is_mutable_pointer() == real_ty.is_mutable_pointer() |
204 | } | |
205 | // rule out everything else | |
206 | _ => false, | |
207 | }; | |
208 | } | |
209 | ||
210 | if sig.abi == real_sig.abi && sig.variadic == real_sig.variadic && | |
211 | sig.inputs_and_output.len() == real_sig.inputs_and_output.len() && | |
212 | sig.inputs_and_output | |
213 | .iter() | |
214 | .zip(real_sig.inputs_and_output) | |
215 | .all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) | |
216 | { | |
217 | // Definitely good. | |
218 | return Ok(true); | |
219 | } | |
220 | ||
221 | if sig.variadic || real_sig.variadic { | |
222 | // We're not touching this | |
223 | return Ok(false); | |
224 | } | |
225 | ||
226 | // We need to allow what comes up when a non-capturing closure is cast to a fn(). | |
227 | match (sig.abi, real_sig.abi) { | |
228 | (Abi::Rust, Abi::RustCall) // check the ABIs. This makes the test here non-symmetric. | |
229 | if check_ty_compat(sig.output(), real_sig.output()) && real_sig.inputs_and_output.len() == 3 => { | |
230 | // First argument of real_sig must be a ZST | |
231 | let fst_ty = real_sig.inputs_and_output[0]; | |
ff7c6d11 | 232 | if self.layout_of(fst_ty)?.is_zst() { |
ea8adc8c XL |
233 | // Second argument must be a tuple matching the argument list of sig |
234 | let snd_ty = real_sig.inputs_and_output[1]; | |
235 | match snd_ty.sty { | |
0531ce1d | 236 | ty::TyTuple(tys) if sig.inputs().len() == tys.len() => |
ea8adc8c XL |
237 | if sig.inputs().iter().zip(tys).all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) { |
238 | return Ok(true) | |
239 | }, | |
240 | _ => {} | |
241 | } | |
242 | } | |
243 | } | |
244 | _ => {} | |
245 | }; | |
246 | ||
247 | // Nope, this doesn't work. | |
248 | return Ok(false); | |
249 | } | |
250 | ||
251 | fn eval_fn_call( | |
252 | &mut self, | |
253 | instance: ty::Instance<'tcx>, | |
ff7c6d11 | 254 | destination: Option<(Place, mir::BasicBlock)>, |
ea8adc8c XL |
255 | args: &[ValTy<'tcx>], |
256 | span: Span, | |
257 | sig: ty::FnSig<'tcx>, | |
258 | ) -> EvalResult<'tcx> { | |
259 | trace!("eval_fn_call: {:#?}", instance); | |
260 | match instance.def { | |
261 | ty::InstanceDef::Intrinsic(..) => { | |
262 | let (ret, target) = match destination { | |
263 | Some(dest) => dest, | |
264 | _ => return err!(Unreachable), | |
265 | }; | |
266 | let ty = sig.output(); | |
ff7c6d11 XL |
267 | let layout = self.layout_of(ty)?; |
268 | M::call_intrinsic(self, instance, args, ret, layout, target)?; | |
ea8adc8c XL |
269 | self.dump_local(ret); |
270 | Ok(()) | |
271 | } | |
272 | // FIXME: figure out why we can't just go through the shim | |
273 | ty::InstanceDef::ClosureOnceShim { .. } => { | |
274 | if M::eval_fn_call(self, instance, destination, args, span, sig)? { | |
275 | return Ok(()); | |
276 | } | |
277 | let mut arg_locals = self.frame().mir.args_iter(); | |
278 | match sig.abi { | |
279 | // closure as closure once | |
280 | Abi::RustCall => { | |
281 | for (arg_local, &valty) in arg_locals.zip(args) { | |
ff7c6d11 | 282 | let dest = self.eval_place(&mir::Place::Local(arg_local))?; |
ea8adc8c XL |
283 | self.write_value(valty, dest)?; |
284 | } | |
285 | } | |
286 | // non capture closure as fn ptr | |
287 | // need to inject zst ptr for closure object (aka do nothing) | |
288 | // and need to pack arguments | |
289 | Abi::Rust => { | |
290 | trace!( | |
291 | "arg_locals: {:?}", | |
292 | self.frame().mir.args_iter().collect::<Vec<_>>() | |
293 | ); | |
294 | trace!("args: {:?}", args); | |
295 | let local = arg_locals.nth(1).unwrap(); | |
296 | for (i, &valty) in args.into_iter().enumerate() { | |
ff7c6d11 | 297 | let dest = self.eval_place(&mir::Place::Local(local).field( |
ea8adc8c XL |
298 | mir::Field::new(i), |
299 | valty.ty, | |
300 | ))?; | |
301 | self.write_value(valty, dest)?; | |
302 | } | |
303 | } | |
304 | _ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi), | |
305 | } | |
306 | Ok(()) | |
307 | } | |
308 | ty::InstanceDef::FnPtrShim(..) | | |
309 | ty::InstanceDef::DropGlue(..) | | |
310 | ty::InstanceDef::CloneShim(..) | | |
311 | ty::InstanceDef::Item(_) => { | |
312 | // Push the stack frame, and potentially be entirely done if the call got hooked | |
313 | if M::eval_fn_call(self, instance, destination, args, span, sig)? { | |
314 | return Ok(()); | |
315 | } | |
316 | ||
317 | // Pass the arguments | |
318 | let mut arg_locals = self.frame().mir.args_iter(); | |
319 | trace!("ABI: {:?}", sig.abi); | |
320 | trace!( | |
321 | "arg_locals: {:?}", | |
322 | self.frame().mir.args_iter().collect::<Vec<_>>() | |
323 | ); | |
324 | trace!("args: {:?}", args); | |
325 | match sig.abi { | |
326 | Abi::RustCall => { | |
327 | assert_eq!(args.len(), 2); | |
328 | ||
329 | { | |
330 | // write first argument | |
331 | let first_local = arg_locals.next().unwrap(); | |
ff7c6d11 | 332 | let dest = self.eval_place(&mir::Place::Local(first_local))?; |
ea8adc8c XL |
333 | self.write_value(args[0], dest)?; |
334 | } | |
335 | ||
336 | // unpack and write all other args | |
ff7c6d11 XL |
337 | let layout = self.layout_of(args[1].ty)?; |
338 | if let ty::TyTuple(..) = args[1].ty.sty { | |
339 | if self.frame().mir.args_iter().count() == layout.fields.count() + 1 { | |
ea8adc8c | 340 | match args[1].value { |
ff7c6d11 XL |
341 | Value::ByRef(ptr, align) => { |
342 | for (i, arg_local) in arg_locals.enumerate() { | |
343 | let field = layout.field(&self, i)?; | |
344 | let offset = layout.fields.offset(i).bytes(); | |
345 | let arg = Value::ByRef(ptr.offset(offset, &self)?, | |
346 | align.min(field.align)); | |
ea8adc8c | 347 | let dest = |
ff7c6d11 | 348 | self.eval_place(&mir::Place::Local(arg_local))?; |
ea8adc8c XL |
349 | trace!( |
350 | "writing arg {:?} to {:?} (type: {})", | |
351 | arg, | |
352 | dest, | |
ff7c6d11 | 353 | field.ty |
ea8adc8c XL |
354 | ); |
355 | let valty = ValTy { | |
356 | value: arg, | |
ff7c6d11 | 357 | ty: field.ty, |
ea8adc8c XL |
358 | }; |
359 | self.write_value(valty, dest)?; | |
360 | } | |
361 | } | |
362 | Value::ByVal(PrimVal::Undef) => {} | |
363 | other => { | |
ff7c6d11 XL |
364 | trace!("{:#?}, {:#?}", other, layout); |
365 | let mut layout = layout; | |
366 | 'outer: loop { | |
367 | for i in 0..layout.fields.count() { | |
368 | let field = layout.field(&self, i)?; | |
369 | if layout.fields.offset(i).bytes() == 0 && layout.size == field.size { | |
370 | layout = field; | |
371 | continue 'outer; | |
372 | } | |
373 | } | |
374 | break; | |
375 | } | |
376 | let dest = self.eval_place(&mir::Place::Local( | |
ea8adc8c XL |
377 | arg_locals.next().unwrap(), |
378 | ))?; | |
379 | let valty = ValTy { | |
380 | value: other, | |
ff7c6d11 | 381 | ty: layout.ty, |
ea8adc8c XL |
382 | }; |
383 | self.write_value(valty, dest)?; | |
384 | } | |
385 | } | |
386 | } else { | |
387 | trace!("manual impl of rust-call ABI"); | |
388 | // called a manual impl of a rust-call function | |
ff7c6d11 XL |
389 | let dest = self.eval_place( |
390 | &mir::Place::Local(arg_locals.next().unwrap()), | |
ea8adc8c XL |
391 | )?; |
392 | self.write_value(args[1], dest)?; | |
393 | } | |
394 | } else { | |
395 | bug!( | |
396 | "rust-call ABI tuple argument was {:#?}, {:#?}", | |
397 | args[1].ty, | |
398 | layout | |
399 | ); | |
400 | } | |
401 | } | |
402 | _ => { | |
403 | for (arg_local, &valty) in arg_locals.zip(args) { | |
ff7c6d11 | 404 | let dest = self.eval_place(&mir::Place::Local(arg_local))?; |
ea8adc8c XL |
405 | self.write_value(valty, dest)?; |
406 | } | |
407 | } | |
408 | } | |
409 | Ok(()) | |
410 | } | |
411 | // cannot use the shim here, because that will only result in infinite recursion | |
412 | ty::InstanceDef::Virtual(_, idx) => { | |
413 | let ptr_size = self.memory.pointer_size(); | |
ff7c6d11 XL |
414 | let ptr_align = self.tcx.data_layout.pointer_align; |
415 | let (ptr, vtable) = self.into_ptr_vtable_pair(args[0].value)?; | |
0531ce1d | 416 | let fn_ptr = self.memory.read_ptr_sized( |
ff7c6d11 XL |
417 | vtable.offset(ptr_size * (idx as u64 + 3), &self)?, |
418 | ptr_align | |
ea8adc8c XL |
419 | )?.to_ptr()?; |
420 | let instance = self.memory.get_fn(fn_ptr)?; | |
421 | let mut args = args.to_vec(); | |
ff7c6d11 | 422 | let ty = self.layout_of(args[0].ty)?.field(&self, 0)?.ty; |
ea8adc8c XL |
423 | args[0].ty = ty; |
424 | args[0].value = ptr.to_value(); | |
425 | // recurse with concrete function | |
426 | self.eval_fn_call(instance, destination, &args, span, sig) | |
427 | } | |
428 | } | |
429 | } | |
430 | } |