terminator.rs 19.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12
use std::borrow::Cow;

13 14
use rustc::mir;
use rustc::ty::{self, Ty};
R
Ralf Jung 已提交
15
use rustc::ty::layout::LayoutOf;
D
Donato Sciarra 已提交
16
use syntax::source_map::Span;
17
use rustc_target::spec::abi::Abi;
18

R
Ralf Jung 已提交
19
use rustc::mir::interpret::{EvalResult, Scalar};
R
Ralf Jung 已提交
20
use super::{
21
    EvalContext, Machine, Value, OpTy, Place, PlaceTy, ValTy, Operand, StackPopCleanup
R
Ralf Jung 已提交
22
};
23

O
Oliver Schneider 已提交
24
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
R
Ralf Jung 已提交
25 26 27 28 29 30 31 32 33
    #[inline]
    pub fn goto_block(&mut self, target: Option<mir::BasicBlock>) -> EvalResult<'tcx> {
        if let Some(target) = target {
            self.frame_mut().block = target;
            self.frame_mut().stmt = 0;
            Ok(())
        } else {
            err!(Unreachable)
        }
34 35
    }

36 37 38
    pub(super) fn eval_terminator(
        &mut self,
        terminator: &mir::Terminator<'tcx>,
39
    ) -> EvalResult<'tcx> {
40
        use rustc::mir::TerminatorKind::*;
41
        match terminator.kind {
S
Scott Olson 已提交
42
            Return => {
R
Ralf Jung 已提交
43
                self.dump_place(self.frame().return_place);
S
Scott Olson 已提交
44 45
                self.pop_stack_frame()?
            }
46

R
Ralf Jung 已提交
47
            Goto { target } => self.goto_block(Some(target))?,
48

R
rustfmt  
Ralf Jung 已提交
49 50 51 52 53 54
            SwitchInt {
                ref discr,
                ref values,
                ref targets,
                ..
            } => {
55
                let discr = self.read_value(self.eval_operand(discr, None)?)?;
R
Ralf Jung 已提交
56
                trace!("SwitchInt({:?})", *discr);
57 58 59 60

                // Branch to the `otherwise` case by default, if no match is found.
                let mut target_block = targets[targets.len() - 1];

O
Oliver Schneider 已提交
61
                for (index, &const_int) in values.iter().enumerate() {
62
                    // Compare using binary_op
B
Bernardo Meurer 已提交
63 64 65 66
                    let const_int = Scalar::Bits {
                        bits: const_int,
                        size: discr.layout.size.bytes() as u8
                    };
R
Ralf Jung 已提交
67 68 69
                    let (res, _) = self.binary_op(mir::BinOp::Eq,
                        discr,
                        ValTy { value: Value::Scalar(const_int.into()), layout: discr.layout }
70
                    )?;
R
Ralf Jung 已提交
71
                    if res.to_bool()? {
72 73 74 75 76
                        target_block = targets[index];
                        break;
                    }
                }

R
Ralf Jung 已提交
77
                self.goto_block(Some(target_block))?;
78 79
            }

R
rustfmt  
Ralf Jung 已提交
80 81 82 83 84 85
            Call {
                ref func,
                ref args,
                ref destination,
                ..
            } => {
R
Ralf Jung 已提交
86 87 88
                let (dest, ret) = match *destination {
                    Some((ref lv, target)) => (Some(self.eval_place(lv)?), Some(target)),
                    None => (None, None),
89
                };
90

91
                let func = self.eval_operand(func, None)?;
R
Ralf Jung 已提交
92
                let (fn_def, sig) = match func.layout.ty.sty {
V
varkor 已提交
93
                    ty::FnPtr(sig) => {
R
Ralf Jung 已提交
94
                        let fn_ptr = self.read_scalar(func)?.to_ptr()?;
95
                        let instance = self.memory.get_fn(fn_ptr)?;
96
                        let instance_ty = instance.ty(*self.tcx);
97
                        match instance_ty.sty {
V
varkor 已提交
98
                            ty::FnDef(..) => {
99
                                let sig = self.tcx.normalize_erasing_late_bound_regions(
R
Ralf Jung 已提交
100
                                    self.param_env,
101 102
                                    &sig,
                                );
R
Ralf Jung 已提交
103
                                let real_sig = instance_ty.fn_sig(*self.tcx);
104
                                let real_sig = self.tcx.normalize_erasing_late_bound_regions(
R
Ralf Jung 已提交
105
                                    self.param_env,
106 107
                                    &real_sig,
                                );
108
                                if !self.check_sig_compat(sig, real_sig)? {
109
                                    return err!(FunctionPointerTyMismatch(real_sig, sig));
110
                                }
R
Ralf Jung 已提交
111
                                (instance, sig)
R
rustfmt  
Ralf Jung 已提交
112
                            }
113
                            _ => bug!("unexpected fn ptr to ty: {:?}", instance_ty),
114
                        }
R
rustfmt  
Ralf Jung 已提交
115
                    }
R
Ralf Jung 已提交
116 117 118 119 120 121 122 123
                    ty::FnDef(def_id, substs) => {
                        let sig = func.layout.ty.fn_sig(*self.tcx);
                        let sig = self.tcx.normalize_erasing_late_bound_regions(
                            self.param_env,
                            &sig,
                        );
                        (self.resolve(def_id, substs)?, sig)
                    },
124
                    _ => {
R
Ralf Jung 已提交
125
                        let msg = format!("can't handle callee of type {:?}", func.layout.ty);
126
                        return err!(Unimplemented(msg));
127
                    }
128
                };
R
Ralf Jung 已提交
129
                let args = self.eval_operands(args)?;
R
rustfmt  
Ralf Jung 已提交
130 131
                self.eval_fn_call(
                    fn_def,
R
Ralf Jung 已提交
132
                    &args[..],
R
Ralf Jung 已提交
133 134
                    dest,
                    ret,
R
rustfmt  
Ralf Jung 已提交
135
                    terminator.source_info.span,
R
Ralf Jung 已提交
136
                    Some(sig),
R
rustfmt  
Ralf Jung 已提交
137
                )?;
138 139
            }

R
rustfmt  
Ralf Jung 已提交
140 141 142 143 144
            Drop {
                ref location,
                target,
                ..
            } => {
145
                // FIXME(CTFE): forbid drop in const eval
O
Oliver Schneider 已提交
146
                let place = self.eval_place(location)?;
R
Ralf Jung 已提交
147
                let ty = place.layout.ty;
148
                trace!("TerminatorKind::drop: {:?}, type {}", location, ty);
O
Oliver Schneider 已提交
149

150
                let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty);
151
                self.drop_in_place(
O
Oliver Schneider 已提交
152
                    place,
R
rustfmt  
Ralf Jung 已提交
153 154
                    instance,
                    terminator.source_info.span,
155
                    target,
R
rustfmt  
Ralf Jung 已提交
156
                )?;
O
Oliver Schneider 已提交
157
            }
158

R
rustfmt  
Ralf Jung 已提交
159 160 161 162 163 164 165
            Assert {
                ref cond,
                expected,
                ref msg,
                target,
                ..
            } => {
166 167
                let cond_val = self.read_value(self.eval_operand(cond, None)?)?
                    .to_scalar()?.to_bool()?;
168
                if expected == cond_val {
R
Ralf Jung 已提交
169
                    self.goto_block(Some(target))?;
170
                } else {
171
                    use rustc::mir::interpret::EvalErrorKind::*;
172
                    return match *msg {
O
Oliver Schneider 已提交
173
                        BoundsCheck { ref len, ref index } => {
174
                            let len = self.read_value(self.eval_operand(len, None)?)
175
                                .expect("can't eval len").to_scalar()?
176
                                .to_bits(self.memory().pointer_size())? as u64;
177
                            let index = self.read_value(self.eval_operand(index, None)?)
178
                                .expect("can't eval index").to_scalar()?
179
                                .to_bits(self.memory().pointer_size())? as u64;
180
                            err!(BoundsCheck { len, index })
R
rustfmt  
Ralf Jung 已提交
181
                        }
182 183
                        Overflow(op) => Err(Overflow(op).into()),
                        OverflowNeg => Err(OverflowNeg.into()),
184 185
                        DivisionByZero => Err(DivisionByZero.into()),
                        RemainderByZero => Err(RemainderByZero.into()),
O
Oliver Schneider 已提交
186 187
                        GeneratorResumedAfterReturn |
                        GeneratorResumedAfterPanic => unimplemented!(),
188
                        _ => bug!(),
R
rustfmt  
Ralf Jung 已提交
189
                    };
190
                }
R
rustfmt  
Ralf Jung 已提交
191
            }
192

O
Oliver Schneider 已提交
193 194
            Yield { .. } => unimplemented!("{:#?}", terminator.kind),
            GeneratorDrop => unimplemented!(),
195 196
            DropAndReplace { .. } => unimplemented!(),
            Resume => unimplemented!(),
D
David Henningsson 已提交
197
            Abort => unimplemented!(),
B
Bernardo Meurer 已提交
198 199 200 201
            FalseEdges { .. } => bug!("should have been eliminated by\
                                      `simplify_branches` mir pass"),
            FalseUnwind { .. } => bug!("should have been eliminated by\
                                       `simplify_branches` mir pass"),
202
            Unreachable => return err!(Unreachable),
203 204 205 206 207
        }

        Ok(())
    }

B
Bernardo Meurer 已提交
208 209
    /// Decides whether it is okay to call the method with signature `real_sig`
    /// using signature `sig`.
R
Ralf Jung 已提交
210
    /// FIXME: This should take into account the platform-dependent ABI description.
211 212 213 214 215
    fn check_sig_compat(
        &mut self,
        sig: ty::FnSig<'tcx>,
        real_sig: ty::FnSig<'tcx>,
    ) -> EvalResult<'tcx, bool> {
216
        fn check_ty_compat<'tcx>(ty: Ty<'tcx>, real_ty: Ty<'tcx>) -> bool {
R
rustfmt  
Ralf Jung 已提交
217 218 219
            if ty == real_ty {
                return true;
            } // This is actually a fast pointer comparison
220 221 222
            return match (&ty.sty, &real_ty.sty) {
                // Permit changing the pointer type of raw pointers and references as well as
                // mutability of raw pointers.
B
Bernardo Meurer 已提交
223
                // FIXME: Should not be allowed when fat pointers are involved.
V
varkor 已提交
224 225
                (&ty::RawPtr(_), &ty::RawPtr(_)) => true,
                (&ty::Ref(_, _, _), &ty::Ref(_, _, _)) => {
R
rustfmt  
Ralf Jung 已提交
226 227
                    ty.is_mutable_pointer() == real_ty.is_mutable_pointer()
                }
228
                // rule out everything else
R
rustfmt  
Ralf Jung 已提交
229 230
                _ => false,
            };
231 232
        }

R
rustfmt  
Ralf Jung 已提交
233
        if sig.abi == real_sig.abi && sig.variadic == real_sig.variadic &&
234
            sig.inputs_and_output.len() == real_sig.inputs_and_output.len() &&
R
rustfmt  
Ralf Jung 已提交
235 236 237 238 239
            sig.inputs_and_output
                .iter()
                .zip(real_sig.inputs_and_output)
                .all(|(ty, real_ty)| check_ty_compat(ty, real_ty))
        {
240 241 242 243 244 245 246 247 248 249 250 251
            // Definitely good.
            return Ok(true);
        }

        if sig.variadic || real_sig.variadic {
            // We're not touching this
            return Ok(false);
        }

        // We need to allow what comes up when a non-capturing closure is cast to a fn().
        match (sig.abi, real_sig.abi) {
            (Abi::Rust, Abi::RustCall) // check the ABIs.  This makes the test here non-symmetric.
B
Bernardo Meurer 已提交
252 253
                if check_ty_compat(sig.output(), real_sig.output())
                    && real_sig.inputs_and_output.len() == 3 => {
254 255
                // First argument of real_sig must be a ZST
                let fst_ty = real_sig.inputs_and_output[0];
256
                if self.layout_of(fst_ty)?.is_zst() {
257 258 259
                    // Second argument must be a tuple matching the argument list of sig
                    let snd_ty = real_sig.inputs_and_output[1];
                    match snd_ty.sty {
V
varkor 已提交
260
                        ty::Tuple(tys) if sig.inputs().len() == tys.len() =>
B
Bernardo Meurer 已提交
261
                            if sig.inputs()
B
Bernardo Meurer 已提交
262 263 264
                                .iter()
                                .zip(tys)
                                .all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) {
265 266 267 268 269 270 271 272 273 274 275 276 277
                                return Ok(true)
                            },
                        _ => {}
                    }
                }
            }
            _ => {}
        };

        // Nope, this doesn't work.
        return Ok(false);
    }

R
Ralf Jung 已提交
278
    /// Call this function -- pushing the stack frame and initializing the arguments.
R
Ralf Jung 已提交
279
    /// `sig` is optional in case of FnPtr/FnDef -- but mandatory for closures!
280 281
    fn eval_fn_call(
        &mut self,
O
Oliver Schneider 已提交
282
        instance: ty::Instance<'tcx>,
R
Ralf Jung 已提交
283
        args: &[OpTy<'tcx>],
R
Ralf Jung 已提交
284 285
        dest: Option<PlaceTy<'tcx>>,
        ret: Option<mir::BasicBlock>,
286
        span: Span,
R
Ralf Jung 已提交
287
        sig: Option<ty::FnSig<'tcx>>,
288
    ) -> EvalResult<'tcx> {
O
Oliver Schneider 已提交
289
        trace!("eval_fn_call: {:#?}", instance);
R
Ralf Jung 已提交
290

O
Oliver Schneider 已提交
291 292
        match instance.def {
            ty::InstanceDef::Intrinsic(..) => {
R
Ralf Jung 已提交
293 294 295
                // The intrinsic itself cannot diverge, so if we got here without a return
                // place... (can happen e.g. for transmute returning `!`)
                let dest = match dest {
O
Oliver Schneider 已提交
296
                    Some(dest) => dest,
R
Ralf Jung 已提交
297
                    None => return err!(Unreachable)
298
                };
R
Ralf Jung 已提交
299 300 301 302 303
                M::call_intrinsic(self, instance, args, dest)?;
                // No stack frame gets pushed, the main loop will just act as if the
                // call completed.
                self.goto_block(ret)?;
                self.dump_place(*dest);
O
Oliver Schneider 已提交
304
                Ok(())
R
rustfmt  
Ralf Jung 已提交
305
            }
R
Ralf Jung 已提交
306
            ty::InstanceDef::ClosureOnceShim { .. } |
307 308
            ty::InstanceDef::FnPtrShim(..) |
            ty::InstanceDef::DropGlue(..) |
309
            ty::InstanceDef::CloneShim(..) |
O
Oliver Schneider 已提交
310
            ty::InstanceDef::Item(_) => {
R
Ralf Jung 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
                let mir = match M::find_fn(self, instance, args, dest, ret)? {
                    Some(mir) => mir,
                    None => return Ok(()),
                };

                let return_place = match dest {
                    Some(place) => *place,
                    None => Place::null(&self),
                };
                self.push_stack_frame(
                    instance,
                    span,
                    mir,
                    return_place,
                    StackPopCleanup::Goto(ret),
                )?;
O
Oliver Schneider 已提交
327

R
Ralf Jung 已提交
328 329 330 331 332 333 334 335 336 337 338 339
                // If we didn't get a signture, ask `fn_sig`
                let sig = sig.unwrap_or_else(|| {
                    let fn_sig = instance.ty(*self.tcx).fn_sig(*self.tcx);
                    self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig)
                });
                assert_eq!(sig.inputs().len(), args.len());
                // We can't test the types, as it is fine if the types are ABI-compatible but
                // not equal.

                // Figure out how to pass which arguments.
                // FIXME: Somehow this is horribly full of special cases here, and codegen has
                // none of that.  What is going on?
R
rustfmt  
Ralf Jung 已提交
340
                trace!(
341 342
                    "ABI: {:?}, args: {:#?}",
                    sig.abi,
R
Ralf Jung 已提交
343 344 345
                    args.iter()
                        .map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
                        .collect::<Vec<_>>()
R
rustfmt  
Ralf Jung 已提交
346
                );
R
Ralf Jung 已提交
347
                trace!(
348 349
                    "spread_arg: {:?}, locals: {:#?}",
                    mir.spread_arg,
R
Ralf Jung 已提交
350 351 352 353 354 355
                    mir.args_iter()
                        .map(|local|
                            (local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
                        )
                        .collect::<Vec<_>>()
                );
O
Oliver Schneider 已提交
356

357 358
                // We have two iterators: Where the arguments come from,
                // and where they go to.
O
Oliver Schneider 已提交
359

360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
                // For where they come from: If the ABI is RustCall, we untuple the
                // last incoming argument.  These do not have the same type,
                // so to keep the code paths uniform we accept an allocation
                // (for RustCall ABI only).
                let args_effective : Cow<[OpTy<'tcx>]> =
                    if sig.abi == Abi::RustCall && !args.is_empty() {
                        // Untuple
                        let (&untuple_arg, args) = args.split_last().unwrap();
                        trace!("eval_fn_call: Will pass last argument by untupling");
                        Cow::from(args.iter().map(|&a| Ok(a))
                            .chain((0..untuple_arg.layout.fields.count()).into_iter()
                                .map(|i| self.operand_field(untuple_arg, i as u64))
                            )
                            .collect::<EvalResult<Vec<OpTy<'tcx>>>>()?)
                    } else {
                        // Plain arg passing
                        Cow::from(args)
                    };
R
Ralf Jung 已提交
378

379 380 381 382 383 384 385 386 387 388 389 390
                // Now we have to spread them out across the callee's locals,
                // taking into account the `spread_arg`.
                let mut args_iter = args_effective.iter();
                let mut local_iter = mir.args_iter();
                // HACK: ClosureOnceShim calls something that expects a ZST as
                // first argument, but the callers do not actually pass that ZST.
                // Codegen doesn't care because ZST arguments do not even exist there.
                match instance.def {
                    ty::InstanceDef::ClosureOnceShim { .. } if sig.abi == Abi::Rust => {
                        let local = local_iter.next().unwrap();
                        let dest = self.eval_place(&mir::Place::Local(local))?;
                        assert!(dest.layout.is_zst());
R
rustfmt  
Ralf Jung 已提交
391
                    }
392 393 394 395 396 397 398 399 400 401
                    _ => {}
                }
                // Now back to norml argument passing.
                while let Some(local) = local_iter.next() {
                    let dest = self.eval_place(&mir::Place::Local(local))?;
                    if Some(local) == mir.spread_arg {
                        // Must be a tuple
                        for i in 0..dest.layout.fields.count() {
                            let dest = self.place_field(dest, i as u64)?;
                            self.copy_op(*args_iter.next().unwrap(), dest)?;
R
Ralf Jung 已提交
402
                        }
403 404 405
                    } else {
                        // Normal argument
                        self.copy_op(*args_iter.next().unwrap(), dest)?;
O
Oliver Schneider 已提交
406 407
                    }
                }
408 409
                // Now we should be done
                assert!(args_iter.next().is_none());
O
Oliver Schneider 已提交
410
                Ok(())
R
rustfmt  
Ralf Jung 已提交
411
            }
412
            // cannot use the shim here, because that will only result in infinite recursion
O
Oliver Schneider 已提交
413 414
            ty::InstanceDef::Virtual(_, idx) => {
                let ptr_size = self.memory.pointer_size();
415
                let ptr_align = self.tcx.data_layout.pointer_align;
416 417
                let ptr = self.ref_to_mplace(self.read_value(args[0])?)?;
                let vtable = ptr.vtable()?;
O
Oliver Schneider 已提交
418
                let fn_ptr = self.memory.read_ptr_sized(
419 420
                    vtable.offset(ptr_size * (idx as u64 + 3), &self)?,
                    ptr_align
R
Ralf Jung 已提交
421
                )?.to_ptr()?;
422
                let instance = self.memory.get_fn(fn_ptr)?;
423 424 425 426

                // We have to patch the self argument, in particular get the layout
                // expected by the actual function. Cannot just use "field 0" due to
                // Box<self>.
427
                let mut args = args.to_vec();
428 429 430
                let pointee = args[0].layout.ty.builtin_deref(true).unwrap().ty;
                let fake_fat_ptr_ty = self.tcx.mk_mut_ptr(pointee);
                args[0].layout = self.layout_of(fake_fat_ptr_ty)?.field(&self, 0)?;
431
                args[0].op = Operand::Immediate(Value::Scalar(ptr.ptr.into())); // strip vtable
432
                trace!("Patched self operand to {:#?}", args[0]);
O
Oliver Schneider 已提交
433
                // recurse with concrete function
R
Ralf Jung 已提交
434
                self.eval_fn_call(instance, &args, dest, ret, span, sig)
R
rustfmt  
Ralf Jung 已提交
435
            }
436 437
        }
    }
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454

    fn drop_in_place(
        &mut self,
        place: PlaceTy<'tcx>,
        instance: ty::Instance<'tcx>,
        span: Span,
        target: mir::BasicBlock,
    ) -> EvalResult<'tcx> {
        trace!("drop_in_place: {:?},\n  {:?}, {:?}", *place, place.layout.ty, instance);
        // We take the address of the object.  This may well be unaligned, which is fine
        // for us here.  However, unaligned accesses will probably make the actual drop
        // implementation fail -- a problem shared by rustc.
        let place = self.force_allocation(place)?;

        let (instance, place) = match place.layout.ty.sty {
            ty::Dynamic(..) => {
                // Dropping a trait object.
455
                self.unpack_dyn_trait(place)?
456 457 458 459 460
            }
            _ => (instance, place),
        };

        let arg = OpTy {
461
            op: Operand::Immediate(place.to_ref()),
462 463 464
            layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
        };

R
Ralf Jung 已提交
465
        let ty = self.tcx.mk_nil(); // return type is ()
466 467 468 469 470 471 472 473 474 475 476
        let dest = PlaceTy::null(&self, self.layout_of(ty)?);

        self.eval_fn_call(
            instance,
            &[arg],
            Some(dest),
            Some(target),
            span,
            None,
        )
    }
O
Oliver Schneider 已提交
477
}